{"record":{"id":"4932d371c4685860","repo":"Mintplex-Labs/anything-llm","slug":"error-message-4932d3","errorCode":null,"errorMessage":"error.message","messagePattern":"error\\.message","errorType":"exception","errorClass":"RetryError","httpStatus":null,"severity":"error","filePath":"server/utils/agents/aibitat/providers/openrouter.js","lineNumber":111,"sourceCode":"\n    try {\n      return await tooledStream(\n        this.client,\n        this.model,\n        messages,\n        functions,\n        eventHandler,\n        { provider: this }\n      );\n    } catch (error) {\n      console.error(error.message, error);\n      if (error instanceof OpenAI.AuthenticationError) throw error;\n      if (\n        error instanceof OpenAI.RateLimitError ||\n        error instanceof OpenAI.InternalServerError ||\n        error instanceof OpenAI.APIError\n      ) {\n        throw new RetryError(error.message);\n      }\n      throw error;\n    }\n  }\n\n  /**\n   * Create a non-streaming completion with tool calling support.\n   * Uses native tool calling when enabled via ENV, otherwise falls back to UnTooled.\n   */\n  async complete(messages, functions = []) {\n    const useNative = await this.supportsNativeToolCalling();\n\n    if (!useNative) {\n      return await UnTooled.prototype.complete.call(\n        this,\n        messages,\n        functions,\n        this.#handleFunctionCallChat.bind(this)","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/server/utils/agents/aibitat/providers/openrouter.js#L93-L129","documentation":"OpenRouterProvider.stream() catches failures from the streaming tool-call request to https://openrouter.ai/api/v1 and rethrows OpenAI RateLimitError, InternalServerError, or any APIError as aibitat's RetryError with the original message; AuthenticationError is rethrown raw; other errors pass through. The raw error is console.error'd first, so the console holds the full object. On OpenRouter, 429 can mean your key's limit OR the routed upstream provider's limit, and a 402 (insufficient credits, PaymentRequiredError extends APIError) also lands in this wrap.","triggerScenarios":"Streaming chat.completions.create with OPENROUTER_API_KEY returning 429 (key rate limit or upstream provider rate limit passed through), 402 when the OpenRouter account is out of credits, 404/400 when this.model (default \"openrouter/auto\" or a pinned slug) is invalid or has no available upstream, 5xx on routing failures.","commonSituations":"Trial credit exhausted so every call returns 402 wrapped as RetryError; ':free' model slugs rate-limited per day; pinned model slug removed from the OpenRouter catalog; upstream provider (e.g. a specific vendor) down while openrouter/auto routes into it.","solutions":["Read the console log for the raw error: 402 -> add credits (retrying never helps), 429 -> check whether it's your key or the upstream model, 404 -> fix the slug","Verify OPENROUTER_API_KEY and account credits in the OpenRouter dashboard","Confirm the model slug exists at openrouter.ai/models (or fall back to openrouter/auto)","For upstream 429s, pin a different provider variant of the same model or wait out the window"],"exampleFix":"# before\nOPENROUTER_API_KEY=<key>   # account has 0 credits -> 402 -> RetryError every call\n\n# after: top up credits, and pin a valid slug\n# agent config: model = \"openai/gpt-4o-mini\"","handlingStrategy":"try-catch","validationCode":"// Pre-flight: key valid, credits available, slug listed\nconst res = await fetch(\"https://openrouter.ai/api/v1/models\", {\n  headers: { Authorization: `Bearer ${process.env.OPENROUTER_API_KEY}` },\n});\nif (!res.ok) throw new Error(`OpenRouter pre-flight failed (${res.status}) — check OPENROUTER_API_KEY`);\n// Also check credits: GET https://openrouter.ai/api/v1/credits","typeGuard":"const { RetryError } = require(\"./server/utils/agents/aibitat/error.js\");\nconst isRetryError = (e) => e instanceof RetryError;\nconst isCreditExhausted = (e) => isRetryError(e) && /402|credit|payment/i.test(String(e.message));","tryCatchPattern":"try {\n  return await provider.stream(messages, functions, eventHandler);\n} catch (error) {\n  if (error instanceof OpenAI.AuthenticationError) throw error;\n  if (isCreditExhausted(error)) throw new Error(\"OpenRouter credits exhausted — top up (not retryable)\");\n  if (error instanceof RetryError && /429|5\\d\\d/.test(error.message)) return withBackoff(retryStream);\n  throw error;\n}","preventionTips":["Check the OpenRouter credits endpoint on a schedule; 402 wraps as RetryError but is never retryable","Pin specific vendor slugs you've verified, or use openrouter/auto with fallbacks configured","For 429s, look at the message to see if it's your key or the upstream provider that's limited, then switch variants","Correlate with the console log — the raw error is printed at this site"],"tags":["llm-provider","openrouter","openai-sdk","retry-error","rate-limit","credits","streaming"],"backgroundTag":"openai-api-error","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}