mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-13 03:58:14 +00:00
Merge e91bb8593d into ad25634905
This commit is contained in:
commit
ff343812d1
2 changed files with 127 additions and 7 deletions
|
|
@ -11,6 +11,8 @@ describe("handleProviderError", () => {
|
|||
const result = handleProviderError(error, providerName)
|
||||
|
||||
expect(result).toBeInstanceOf(Error)
|
||||
// Message should be prefixed with status code for UI parsing
|
||||
expect(result.message).toMatch(/^401/)
|
||||
expect(result.message).toContain("TestProvider completion error")
|
||||
expect((result as any).status).toBe(401)
|
||||
})
|
||||
|
|
@ -257,6 +259,111 @@ describe("handleProviderError", () => {
|
|||
expect(result.message).toContain("Invalid API key")
|
||||
})
|
||||
})
|
||||
|
||||
describe("HTTP status code prefixing for UI parsing", () => {
|
||||
it("should prefix error message with status code 402 (Payment Required)", () => {
|
||||
const error = new Error("Insufficient balance") as any
|
||||
error.status = 402
|
||||
|
||||
const result = handleProviderError(error, "DeepSeek")
|
||||
|
||||
// Message should start with status code for UI to parse
|
||||
expect(result.message).toMatch(/^402/)
|
||||
expect(result.message).toContain("DeepSeek completion error")
|
||||
expect(result.message).toContain("Insufficient balance")
|
||||
expect((result as any).status).toBe(402)
|
||||
})
|
||||
|
||||
it("should prefix error message with status code 401 (Unauthorized)", () => {
|
||||
const error = new Error("Invalid API key") as any
|
||||
error.status = 401
|
||||
|
||||
const result = handleProviderError(error, "OpenAI")
|
||||
|
||||
expect(result.message).toMatch(/^401/)
|
||||
expect(result.message).toContain("OpenAI completion error")
|
||||
expect((result as any).status).toBe(401)
|
||||
})
|
||||
|
||||
it("should prefix error message with status code 403 (Forbidden)", () => {
|
||||
const error = new Error("Access denied") as any
|
||||
error.status = 403
|
||||
|
||||
const result = handleProviderError(error, "Anthropic")
|
||||
|
||||
expect(result.message).toMatch(/^403/)
|
||||
expect(result.message).toContain("Anthropic completion error")
|
||||
expect((result as any).status).toBe(403)
|
||||
})
|
||||
|
||||
it("should prefix error message with status code 429 (Rate Limit)", () => {
|
||||
const error = new Error("Too many requests") as any
|
||||
error.status = 429
|
||||
|
||||
const result = handleProviderError(error, "Mistral")
|
||||
|
||||
expect(result.message).toMatch(/^429/)
|
||||
expect(result.message).toContain("Mistral completion error")
|
||||
expect((result as any).status).toBe(429)
|
||||
})
|
||||
|
||||
it("should prefix error message with status code 500 (Server Error)", () => {
|
||||
const error = new Error("Internal server error") as any
|
||||
error.status = 500
|
||||
|
||||
const result = handleProviderError(error, "Groq")
|
||||
|
||||
expect(result.message).toMatch(/^500/)
|
||||
expect(result.message).toContain("Groq completion error")
|
||||
expect((result as any).status).toBe(500)
|
||||
})
|
||||
|
||||
it("should not prefix with status code when using custom messageTransformer", () => {
|
||||
const error = new Error("Payment required") as any
|
||||
error.status = 402
|
||||
|
||||
const result = handleProviderError(error, "TestProvider", {
|
||||
messageTransformer: (msg) => `Custom: ${msg}`,
|
||||
})
|
||||
|
||||
// Custom transformer should be used as-is
|
||||
expect(result.message).toBe("Custom: Payment required")
|
||||
// Status should still be preserved
|
||||
expect((result as any).status).toBe(402)
|
||||
})
|
||||
|
||||
it("should not prefix with status code for non-HTTP errors (no status)", () => {
|
||||
const error = new Error("Network timeout")
|
||||
|
||||
const result = handleProviderError(error, "TestProvider")
|
||||
|
||||
// Should not have status prefix
|
||||
expect(result.message).not.toMatch(/^\d{3}/)
|
||||
expect(result.message).toBe("TestProvider completion error: Network timeout")
|
||||
})
|
||||
|
||||
it("should not prefix with status code for status < 400", () => {
|
||||
const error = new Error("Redirect") as any
|
||||
error.status = 301
|
||||
|
||||
const result = handleProviderError(error, "TestProvider")
|
||||
|
||||
// Should not have status prefix for non-error status codes
|
||||
expect(result.message).not.toMatch(/^301/)
|
||||
expect(result.message).toBe("TestProvider completion error: Redirect")
|
||||
})
|
||||
|
||||
it("should work with custom messagePrefix option", () => {
|
||||
const error = new Error("No funds") as any
|
||||
error.status = 402
|
||||
|
||||
const result = handleProviderError(error, "DeepSeek", { messagePrefix: "streaming" })
|
||||
|
||||
expect(result.message).toMatch(/^402/)
|
||||
expect(result.message).toContain("DeepSeek streaming error")
|
||||
expect(result.message).toContain("No funds")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe("handleOpenAIError (backward compatibility)", () => {
|
||||
|
|
@ -267,6 +374,8 @@ describe("handleOpenAIError (backward compatibility)", () => {
|
|||
const result = handleOpenAIError(error, "OpenAI")
|
||||
|
||||
expect(result).toBeInstanceOf(Error)
|
||||
// Should be prefixed with status code for UI parsing
|
||||
expect(result.message).toMatch(/^500/)
|
||||
expect(result.message).toContain("OpenAI completion error")
|
||||
expect((result as any).status).toBe(500)
|
||||
})
|
||||
|
|
@ -277,7 +386,8 @@ describe("handleOpenAIError (backward compatibility)", () => {
|
|||
|
||||
const result = handleOpenAIError(error, "Roo Code Cloud")
|
||||
|
||||
expect(result.message).toBe("Roo Code Cloud completion error: Authentication failed")
|
||||
// Message should now include status prefix for UI parsing
|
||||
expect(result.message).toBe("401 - Roo Code Cloud completion error: Authentication failed")
|
||||
expect((result as any).status).toBe(401)
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -49,13 +49,14 @@ export function handleProviderError(
|
|||
if (error instanceof Error) {
|
||||
const anyErr = error as any
|
||||
const msg = anyErr?.error?.metadata?.raw || error.message || ""
|
||||
const status = anyErr.status
|
||||
|
||||
// Log the original error details for debugging
|
||||
console.error(`[${providerName}] API error:`, {
|
||||
message: msg,
|
||||
name: error.name,
|
||||
stack: error.stack,
|
||||
status: anyErr.status,
|
||||
status,
|
||||
})
|
||||
|
||||
let wrapped: Error
|
||||
|
|
@ -66,17 +67,26 @@ export function handleProviderError(
|
|||
wrapped = new Error(i18n.t("common:errors.api.invalidKeyInvalidChars"))
|
||||
} else {
|
||||
// Apply custom transformer if provided, otherwise use default format
|
||||
const finalMessage = options?.messageTransformer
|
||||
? options.messageTransformer(msg)
|
||||
: `${providerName} ${messagePrefix} error: ${msg}`
|
||||
// IMPORTANT: When HTTP status is available, prefix the message with the status code
|
||||
// This allows the UI (ChatRow.tsx) to parse the status and show user-friendly error messages
|
||||
// for known status codes (e.g., 402 = insufficient balance, 429 = rate limit)
|
||||
let finalMessage: string
|
||||
if (options?.messageTransformer) {
|
||||
finalMessage = options.messageTransformer(msg)
|
||||
} else if (typeof status === "number" && status >= 400) {
|
||||
// Prefix with status code for UI parsing (e.g., "402 - DeepSeek completion error: ...")
|
||||
finalMessage = `${status} - ${providerName} ${messagePrefix} error: ${msg}`
|
||||
} else {
|
||||
finalMessage = `${providerName} ${messagePrefix} error: ${msg}`
|
||||
}
|
||||
wrapped = new Error(finalMessage)
|
||||
}
|
||||
|
||||
// Preserve HTTP status and structured details for retry/backoff + UI
|
||||
// These fields are used by Task.backoffAndAnnounce() and ChatRow/ErrorRow
|
||||
// to provide status-aware error messages and handling
|
||||
if (anyErr.status !== undefined) {
|
||||
;(wrapped as any).status = anyErr.status
|
||||
if (status !== undefined) {
|
||||
;(wrapped as any).status = status
|
||||
}
|
||||
if (anyErr.errorDetails !== undefined) {
|
||||
;(wrapped as any).errorDetails = anyErr.errorDetails
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue