fix
This commit is contained in:
@@ -67,11 +67,17 @@ func truncateRunes(s string, max int) string {
|
||||
return s
|
||||
}
|
||||
|
||||
const (
|
||||
publicErrProcessingFailed = "processing_failed"
|
||||
publicErrProviderUnavailable = "provider_unavailable"
|
||||
)
|
||||
|
||||
// TruncateError returns a safe, short error string for DB storage / API clients.
|
||||
// Secret-like substrings and logredact matches become an opaque message so
|
||||
// job step_progress notes and v1 item errors cannot leak keys/JWTs/DSNs/emails.
|
||||
// Common provider transport failures are rewritten to short user-facing text
|
||||
// (no dial URLs / Go net strings) while preserving unrelated provider messages.
|
||||
// (no dial URLs / Go net strings). Remaining provider internals map to stable
|
||||
// public codes (processing_failed / provider_unavailable).
|
||||
func TruncateError(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
@@ -94,20 +100,101 @@ func TruncateError(err error) string {
|
||||
return friendly
|
||||
}
|
||||
// Drop retry-exhaustion wrapper when the inner message is already clear.
|
||||
cleaned := redacted
|
||||
cleaned := stripOpenAIRetryPrefix(redacted)
|
||||
if friendly := classifyProviderError(cleaned); friendly != "" {
|
||||
return friendly
|
||||
}
|
||||
if code := mapPublicErrorCode(cleaned); code != "" {
|
||||
return code
|
||||
}
|
||||
return truncateRunes(cleaned, 500)
|
||||
}
|
||||
|
||||
// PublicV1Error maps a stored job/item error to a stable public v1 string.
|
||||
// Dashboard notes / step_progress may still carry operator detail; public JSON must not.
|
||||
func PublicV1Error(msg string) string {
|
||||
msg = strings.TrimSpace(msg)
|
||||
if msg == "" {
|
||||
return ""
|
||||
}
|
||||
switch msg {
|
||||
case publicErrProcessingFailed, publicErrProviderUnavailable,
|
||||
"Product data not available", "Processing failed",
|
||||
"provider error (details redacted)":
|
||||
return msg
|
||||
}
|
||||
if strings.HasPrefix(msg, "processing.job.error.") {
|
||||
return msg
|
||||
}
|
||||
if strings.Contains(strings.ToLower(msg), "insufficient credits") {
|
||||
return msg
|
||||
}
|
||||
if friendly := classifyProviderError(msg); friendly != "" {
|
||||
return friendly
|
||||
}
|
||||
cleaned := stripOpenAIRetryPrefix(msg)
|
||||
if friendly := classifyProviderError(cleaned); friendly != "" {
|
||||
return friendly
|
||||
}
|
||||
if code := mapPublicErrorCode(cleaned); code != "" {
|
||||
return code
|
||||
}
|
||||
if looksLikeInternalProviderError(cleaned) {
|
||||
return publicErrProcessingFailed
|
||||
}
|
||||
return truncateRunes(cleaned, 500)
|
||||
}
|
||||
|
||||
func stripOpenAIRetryPrefix(msg string) string {
|
||||
cleaned := msg
|
||||
for _, prefix := range []string{
|
||||
"openai retries exhausted: ",
|
||||
"openai embedding retries exhausted: ",
|
||||
} {
|
||||
if strings.HasPrefix(strings.ToLower(cleaned), prefix) {
|
||||
cleaned = strings.TrimSpace(cleaned[len(prefix):])
|
||||
break
|
||||
return strings.TrimSpace(cleaned[len(prefix):])
|
||||
}
|
||||
}
|
||||
if friendly := classifyProviderError(cleaned); friendly != "" {
|
||||
return friendly
|
||||
return cleaned
|
||||
}
|
||||
|
||||
func mapPublicErrorCode(msg string) string {
|
||||
lower := strings.ToLower(strings.TrimSpace(msg))
|
||||
if lower == "" {
|
||||
return ""
|
||||
}
|
||||
return truncateRunes(cleaned, 500)
|
||||
switch {
|
||||
case strings.Contains(lower, "max_tokens"),
|
||||
strings.Contains(lower, "length-capped"):
|
||||
return publicErrProcessingFailed
|
||||
case strings.Contains(lower, "openai unset"),
|
||||
strings.Contains(lower, "platform openai"):
|
||||
return publicErrProviderUnavailable
|
||||
case strings.Contains(lower, "ai_enhance: skipped"):
|
||||
return publicErrProcessingFailed
|
||||
case strings.Contains(lower, "unavailable"),
|
||||
strings.Contains(lower, "http 503"),
|
||||
strings.Contains(lower, "overloaded"):
|
||||
return publicErrProviderUnavailable
|
||||
case strings.Contains(lower, "openai"),
|
||||
strings.Contains(lower, "gpt-"),
|
||||
strings.Contains(lower, " for model"):
|
||||
return publicErrProcessingFailed
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func looksLikeInternalProviderError(msg string) bool {
|
||||
lower := strings.ToLower(msg)
|
||||
for _, n := range []string{
|
||||
"openai", "max_tokens", "length-capped", "gpt-", "ai_enhance",
|
||||
"finish_reason", "chat/completions",
|
||||
} {
|
||||
if strings.Contains(lower, n) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// classifyProviderError maps common OpenAI-compatible transport/auth failures
|
||||
|
||||
Reference in New Issue
Block a user