{"record":{"id":"4a261784271d12f8","repo":"chatboxai/chatbox","slug":"error-from-this-name-context-extractstream","errorCode":null,"errorMessage":"Error from ${this.name}${context}: ${extractStreamErrorMessage(error)}","messagePattern":"Error from (.+?)(.+?): (.+?)","errorType":"http","errorClass":"ApiError","httpStatus":null,"severity":"error","filePath":"src/shared/models/abstract-ai-sdk.ts","lineNumber":742,"sourceCode":"    if (\n      RetryError.isInstance(error) &&\n      (error.lastError instanceof ApiError || APICallError.isInstance(error.lastError))\n    ) {\n      return this.handleError(error.lastError, context)\n    }\n    if (APICallError.isInstance(error)) {\n      const responseBody = this.sanitizeResponseBody(error.statusCode, error.responseBody)\n      throw new ApiError(`Error from ${this.name}${context}`, responseBody, error.statusCode)\n    }\n    if (error instanceof ApiError) {\n      throw error\n    }\n    if (error instanceof ChatboxAIAPIError) {\n      throw error\n    }\n    // Mid-stream provider errors are often plain objects ({ message, type, code });\n    // interpolating them yields \"[object Object]\".\n    throw new ApiError(`Error from ${this.name}${context}: ${extractStreamErrorMessage(error)}`)\n  }\n\n  /**\n   * Sanitize HTML error pages (e.g., 502/503/504 gateway errors) from response bodies.\n   */\n  private sanitizeResponseBody(statusCode: number | undefined, responseBody: string | undefined): string {\n    if (!responseBody) return ''\n    const trimmed = responseBody.trimStart().toLowerCase()\n    if (trimmed.startsWith('<!doctype') || trimmed.startsWith('<html')) {\n      const statusMessages: Record<number, string> = {\n        502: 'Bad Gateway',\n        503: 'Service Unavailable',\n        504: 'Gateway Timeout',\n      }\n      const statusMsg = statusCode ? statusMessages[statusCode] || `HTTP ${statusCode}` : 'Server Error'\n      return `${statusMsg} - The server returned an HTML error page instead of a valid response.`\n    }\n    return responseBody","sourceCodeStart":724,"sourceCodeEnd":760,"githubUrl":"https://github.com/chatboxai/chatbox/blob/81571269addb6bafb589a920b2883f1e1e084fd1/src/shared/models/abstract-ai-sdk.ts#L724-L760","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nthrow new ApiError(`Error from ${this.name}${context}: ${extractStreamErrorMessage(error)}`)\n// after — preserve statusCode when present and avoid 'unknown error' loss\nconst msg = extractStreamErrorMessage(error)\nconst status = (error && typeof error === 'object' && 'status' in error) ? Number(error.status) : undefined\nthrow new ApiError(`Error from ${this.name}${context}: ${msg}`, undefined, status)","handlingStrategy":"try-catch","validationCode":"// Validate stream setup before opening the connection to reduce opaque mid-stream errors.\nfunction assertStreamPreconditions(signal: AbortSignal | undefined, messages: unknown[]): void {\n  if (signal?.aborted) throw new Error('AbortSignal already aborted before stream start')\n  if (!Array.isArray(messages) || messages.length === 0) throw new Error('No messages to stream')\n}","typeGuard":"function isOpaqueStreamError(e: unknown): boolean {\n  return e instanceof ApiError && /^Error from .+:/.test(e.message) && !e.statusCode\n}","tryCatchPattern":"try {\n  return await sdk.chatStream(messages, opts)\n} catch (e) {\n  if (e instanceof ApiError && /Error from .+:/.test(e.message)) {\n    // preserve already-received tokens; offer 'continue'\n    offerContinuePartialTurn()\n    return\n  }\n  throw e\n}","preventionTips":["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."],"tags":["ai-sdk","streaming","catch-all","error-funnel"],"backgroundTag":null,"analyzedSha":"81571269addb6bafb589a920b2883f1e1e084fd1","analyzedAt":"2026-08-12T21:51:44.981Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}