chatboxai/chatbox · error · ApiError

Error from ${this.name}${context}

Error message

Error from ${this.name}${context}

What it means

Thrown by AbstractAISDK.handleError when the SDK raises an APICallError: the error is re-wrapped as an ApiError named after the provider (this.name) plus an optional context string, carrying the sanitized response body and the original statusCode. The sanitize step strips HTML gateway pages (502/503/504) down to their status message.

Source

Thrown at src/shared/models/abstract-ai-sdk.ts:732

    return { currentTextPart, currentReasoningPart }
  }

  private handleError(error: unknown, context: string = ''): never {
    // When a retried attempt fails again, ai-retry wraps the failure in the AI
    // SDK's RetryError; unwrap so the original error's message/statusCode/
    // responseBody reach the UI instead of the generic "Failed after N attempts".
    // (Relies on RetryError.lastError holding the raw thrown error — re-check on
    // ai-retry upgrades.)
    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()

View on GitHub (pinned to 81571269ad)

Solutions

  1. Read ApiError.statusCode and ApiError.responseBody on the caught error — they carry the provider's actual failure reason.
  2. Map statusCode to remediation: 401/403 → fix key/scope; 429 → back off; 400 → inspect responseBody for the invalid parameter; 5xx → retry then check provider status.
  3. If responseBody looks sanitized (e.g. 'Bad Gateway'), the issue is upstream of the API — retry or switch endpoints.
  4. When adding context, pass a short context string into handleError so the message indicates which operation failed (e.g. ' during chatStream').

Example fix

// before
if (APICallError.isInstance(error)) {
  const responseBody = this.sanitizeResponseBody(error.statusCode, error.responseBody)
  throw new ApiError(`Error from ${this.name}${context}`, responseBody, error.statusCode)
}
// after — preserve the upstream message when sanitization left it intact
if (APICallError.isInstance(error)) {
  const responseBody = this.sanitizeResponseBody(error.statusCode, error.responseBody)
  const detail = responseBody || error.message || ''
  throw new ApiError(`Error from ${this.name}${context}: ${detail}`, responseBody, error.statusCode)
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight validate auth/model before the SDK call to reduce APICallError surface.
async function providerCallLikelyToSucceed(opts: { apiKey?: string; modelId?: string }): Promise<boolean> {
  return Boolean(opts.apiKey && opts.modelId)
}

Type guard

function isApiErrorFromProvider(e: unknown, providerName: string): e is ApiError {
  return e instanceof ApiError && e.message.startsWith(`Error from ${providerName}`)
}

Try / catch

try {
  return await sdk.chat(messages, opts)
} catch (e) {
  if (e instanceof ApiError && typeof e.statusCode === 'number') {
    if (e.statusCode === 401) rotateApiKey()
    else if (e.statusCode === 429) return retryWithBackoff(() => sdk.chat(messages, opts))
    else showUser(`${providerName}: ${e.responseBody || e.message}`)
    return
  }
  throw e
}

Prevention

When it happens

Trigger: An SDK call (chat/embed/image) rejected with an APICallError that is not already an ApiError and not wrapped in a RetryError. handleError is the central error funnel; it converts APICallError → ApiError so the UI sees a uniform shape with provider name, body, and status.

Common situations: Provider returned a structured error JSON the AI SDK surfaced as APICallError (4xx with body); upstream gateway returned HTML (sanitized to 'Bad Gateway' etc.); the request reached the provider but was rejected for auth/quota/validation reasons.

Related errors


AI-assisted analysis of chatboxai/chatbox@81571269ad (2026-08-12). Data as JSON: /api/errors/c303bced3afc91df. Report an issue: GitHub.