fix(ops): validate error_type against known whitelist before classification
Upstream proxies (account 4, 112) return `"<nil>"` as the error.type in
their JSON responses — a Go fmt.Sprintf("%v", nil) artifact. Since
`normalizeOpsErrorType` only checked for empty string, the literal
"<nil>" passed through and poisoned the entire classification chain:
error_phase was misclassified as "internal" (instead of "request"),
severity was inflated to P2, and the stored error_type was meaningless.
Add `isKnownOpsErrorType` whitelist so any unrecognised type falls
through to the code-based or default "api_error" classification.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -939,8 +939,29 @@ func guessPlatformFromPath(path string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isKnownOpsErrorType returns true if t is a recognized error type used by the
|
||||||
|
// ops classification pipeline. Upstream proxies sometimes return garbage values
|
||||||
|
// (e.g. the Go-serialized literal "<nil>") which would pollute phase/severity
|
||||||
|
// classification if accepted blindly.
|
||||||
|
func isKnownOpsErrorType(t string) bool {
|
||||||
|
switch t {
|
||||||
|
case "invalid_request_error",
|
||||||
|
"authentication_error",
|
||||||
|
"rate_limit_error",
|
||||||
|
"billing_error",
|
||||||
|
"subscription_error",
|
||||||
|
"upstream_error",
|
||||||
|
"overloaded_error",
|
||||||
|
"api_error",
|
||||||
|
"not_found_error",
|
||||||
|
"forbidden_error":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func normalizeOpsErrorType(errType string, code string) string {
|
func normalizeOpsErrorType(errType string, code string) string {
|
||||||
if errType != "" {
|
if errType != "" && isKnownOpsErrorType(errType) {
|
||||||
return errType
|
return errType
|
||||||
}
|
}
|
||||||
switch strings.TrimSpace(code) {
|
switch strings.TrimSpace(code) {
|
||||||
|
|||||||
@@ -214,3 +214,60 @@ func TestOpsErrorLoggerMiddleware_DoesNotBreakOuterMiddlewares(t *testing.T) {
|
|||||||
})
|
})
|
||||||
require.Equal(t, http.StatusNoContent, rec.Code)
|
require.Equal(t, http.StatusNoContent, rec.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIsKnownOpsErrorType(t *testing.T) {
|
||||||
|
known := []string{
|
||||||
|
"invalid_request_error",
|
||||||
|
"authentication_error",
|
||||||
|
"rate_limit_error",
|
||||||
|
"billing_error",
|
||||||
|
"subscription_error",
|
||||||
|
"upstream_error",
|
||||||
|
"overloaded_error",
|
||||||
|
"api_error",
|
||||||
|
"not_found_error",
|
||||||
|
"forbidden_error",
|
||||||
|
}
|
||||||
|
for _, k := range known {
|
||||||
|
require.True(t, isKnownOpsErrorType(k), "expected known: %s", k)
|
||||||
|
}
|
||||||
|
|
||||||
|
unknown := []string{"<nil>", "null", "", "random_error", "some_new_type", "<nil>\u003e"}
|
||||||
|
for _, u := range unknown {
|
||||||
|
require.False(t, isKnownOpsErrorType(u), "expected unknown: %q", u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNormalizeOpsErrorType(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
errType string
|
||||||
|
code string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
// Known types pass through.
|
||||||
|
{"known invalid_request_error", "invalid_request_error", "", "invalid_request_error"},
|
||||||
|
{"known rate_limit_error", "rate_limit_error", "", "rate_limit_error"},
|
||||||
|
{"known upstream_error", "upstream_error", "", "upstream_error"},
|
||||||
|
|
||||||
|
// Unknown/garbage types are rejected and fall through to code-based or default.
|
||||||
|
{"nil literal from upstream", "<nil>", "", "api_error"},
|
||||||
|
{"null string", "null", "", "api_error"},
|
||||||
|
{"random string", "something_weird", "", "api_error"},
|
||||||
|
|
||||||
|
// Unknown type but known code still maps correctly.
|
||||||
|
{"nil with INSUFFICIENT_BALANCE code", "<nil>", "INSUFFICIENT_BALANCE", "billing_error"},
|
||||||
|
{"nil with USAGE_LIMIT_EXCEEDED code", "<nil>", "USAGE_LIMIT_EXCEEDED", "subscription_error"},
|
||||||
|
|
||||||
|
// Empty type falls through to code-based mapping.
|
||||||
|
{"empty type with balance code", "", "INSUFFICIENT_BALANCE", "billing_error"},
|
||||||
|
{"empty type with subscription code", "", "SUBSCRIPTION_NOT_FOUND", "subscription_error"},
|
||||||
|
{"empty type no code", "", "", "api_error"},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
got := normalizeOpsErrorType(tt.errType, tt.code)
|
||||||
|
require.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user