chatboxai/chatbox · error · ApiError
Error from ${this.name}${context}: ${extractStreamErrorMessa
Error message
Error from ${this.name}${context}: ${extractStreamErrorMessage(error)} What it means
The terminal branch of AbstractAISDK.handleError: thrown for any error that is not a RetryError-wrapped ApiError/APICallError, not a bare ApiError, not a ChatboxAIAPIError. It exists because mid-stream provider errors frequently arrive as plain objects ({message,type,code}) which would interpolate to '[object Object]'; extractStreamErrorMessage unwraps them into a readable string before the ApiError is built.
Source
Thrown at src/shared/models/abstract-ai-sdk.ts:742
if (
RetryError.isInstance(error) &&
(error.lastError instanceof ApiError || APICallError.isInstance(error.lastError))
) {
return this.handleError(error.lastError, context)
}
if (APICallError.isInstance(error)) {
const responseBody = this.sanitizeResponseBody(error.statusCode, error.responseBody)
throw new ApiError(`Error from ${this.name}${context}`, responseBody, error.statusCode)
}
if (error instanceof ApiError) {
throw error
}
if (error instanceof ChatboxAIAPIError) {
throw error
}
// Mid-stream provider errors are often plain objects ({ message, type, code });
// interpolating them yields "[object Object]".
throw new ApiError(`Error from ${this.name}${context}: ${extractStreamErrorMessage(error)}`)
}
/**
* Sanitize HTML error pages (e.g., 502/503/504 gateway errors) from response bodies.
*/
private sanitizeResponseBody(statusCode: number | undefined, responseBody: string | undefined): string {
if (!responseBody) return ''
const trimmed = responseBody.trimStart().toLowerCase()
if (trimmed.startsWith('<!doctype') || trimmed.startsWith('<html')) {
const statusMessages: Record<number, string> = {
502: 'Bad Gateway',
503: 'Service Unavailable',
504: 'Gateway Timeout',
}
const statusMsg = statusCode ? statusMessages[statusCode] || `HTTP ${statusCode}` : 'Server Error'
return `${statusMsg} - The server returned an HTML error page instead of a valid response.`
}
return responseBodyView on GitHub (pinned to 81571269ad)
Solutions
- Inspect the caught ApiError.message after extractStreamErrorMessage — it should contain the upstream text; if it's still generic, log the raw error value at the handleError entry to capture the unhandled shape.
- For mid-stream network blips, the chat UI should preserve already-received tokens and offer a 'continue' rather than discarding the turn.
- If you see this frequently for a specific provider, extend handleError with another instanceof branch (and extractStreamErrorMessage with another shape) so the error is classified earlier.
- Check that the AbortSignal you pass isn't being triggered by an unrelated error (e.g. a CSP/fetch policy) — those arrive here as opaque errors.
Example fix
// before
throw new ApiError(`Error from ${this.name}${context}: ${extractStreamErrorMessage(error)}`)
// after — preserve statusCode when present and avoid 'unknown error' loss
const msg = extractStreamErrorMessage(error)
const status = (error && typeof error === 'object' && 'status' in error) ? Number(error.status) : undefined
throw new ApiError(`Error from ${this.name}${context}: ${msg}`, undefined, status) Defensive patterns
Strategy: try-catch
Validate before calling
// Validate stream setup before opening the connection to reduce opaque mid-stream errors.
function assertStreamPreconditions(signal: AbortSignal | undefined, messages: unknown[]): void {
if (signal?.aborted) throw new Error('AbortSignal already aborted before stream start')
if (!Array.isArray(messages) || messages.length === 0) throw new Error('No messages to stream')
} Type guard
function isOpaqueStreamError(e: unknown): boolean {
return e instanceof ApiError && /^Error from .+:/.test(e.message) && !e.statusCode
} Try / catch
try {
return await sdk.chatStream(messages, opts)
} catch (e) {
if (e instanceof ApiError && /Error from .+:/.test(e.message)) {
// preserve already-received tokens; offer 'continue'
offerContinuePartialTurn()
return
}
throw e
} Prevention
- Don't reuse an AbortSignal that may be triggered by unrelated errors (CSP, fetch policy).
- Extend handleError with new instanceof branches as new opaque shapes appear, so errors get classified earlier.
- Log the raw error value at handleError's entry so unhandled shapes are diagnosable.
When it happens
Trigger: A streaming chat call rejected with an opaque value: a plain object from an SSE error frame, a non-Error thrown by a transform, an AbortSignal-triggered rejection that wasn't an ApiError, or an unexpected exception type inside the SDK pipeline. extractStreamErrorMessage attempts to coerce it to a string.
Common situations: Provider sends a mid-stream error event as JSON the SDK doesn't classify; a downstream transformer throws a plain object; a network blip mid-stream produces a TypeError; an unexpected Content Security Policy violation aborts the stream.
Related errors
- Error from ${this.name}${context}
- No response body
- third_party_parser_failed
- Provider doesnt support image generation
- ${parsed.error.message || 'Upstream Google Generative AI str
AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12).
Data as JSON: /api/errors/4a261784271d12f8.
Report an issue: GitHub.