Mintplex-Labs/anything-llm · error · Error

Invalid response body returned from OpenRouter: ${result.out

Error message

Invalid response body returned from OpenRouter: ${result.output?.error?.message || "Unknown error"} ${result.output?.error?.code || "Unknown code"}

What it means

Post-success structural validation: the chat.completions.create call resolved without throwing, but the body lacks a 'choices' array (or it is empty). OpenRouter sometimes returns 200 with an inline error object; the message includes result.output.error.message and result.output.error.code when present. This is the catch for shape problems the SDK did not reject on.

Source

Thrown at server/utils/AiProviders/openRouter/index.js:267

        .create({
          model: this.model,
          messages,
          temperature,
          // This is an OpenRouter specific option that allows us to get the reasoning text
          // before the token text.
          include_reasoning: true,
          user: user?.id ? `user_${user.id}` : "",
        })
        .catch((e) => {
          throw new Error(e.message);
        })
    );

    if (
      !result?.output?.hasOwnProperty("choices") ||
      result?.output?.choices?.length === 0
    )
      throw new Error(
        `Invalid response body returned from OpenRouter: ${result.output?.error?.message || "Unknown error"} ${result.output?.error?.code || "Unknown code"}`
      );

    return {
      textResponse: this.#parseReasoningFromResponse(result.output.choices[0]),
      metrics: {
        prompt_tokens: result.output.usage.prompt_tokens || 0,
        completion_tokens: result.output.usage.completion_tokens || 0,
        total_tokens: result.output.usage.total_tokens || 0,
        outputTps: result.output.usage.completion_tokens / result.duration,
        duration: result.duration,
        model: this.model,
        provider: this.className,
        timestamp: new Date(),
      },
    };
  }

View on GitHub (pinned to 526360e320)

Solutions

  1. Inspect the embedded error.message/error.code in the thrown text for the upstream cause.
  2. Retry once — many of these are transient upstream blips.
  3. Switch to a different model/provider slug for the same capability.
  4. Check OpenRouter status (status.openrouter.ai) for provider outages.

Example fix

// before
if (!result?.output?.hasOwnProperty('choices') || result?.output?.choices?.length === 0)
  throw new Error(`Invalid response body returned from OpenRouter: ${result.output?.error?.message || 'Unknown error'} ${result.output?.error?.code || 'Unknown code'}`);

// after - treat upstream errors as retryable signals with a code
const body = result?.output;
if (!body?.hasOwnProperty('choices') || body?.choices?.length === 0) {
  const err = new Error(`OpenRouter returned no choices: ${body?.error?.message || 'Unknown error'} (${body?.error?.code || 'Unknown code'})`);
  err.code = body?.error?.code || 'NO_CHOICES';
  err.retryable = true;
  throw err;
}
Defensive patterns

Strategy: fallback

Validate before calling

const hasOpenRouterChoices = (o) =>
  !!o && typeof o === 'object' && Array.isArray(o.choices) && o.choices.length > 0;

Type guard

const hasOpenRouterChoices = (o) =>
  !!o && typeof o === 'object' && Array.isArray(o.choices) && o.choices.length > 0;

Try / catch

try {
  const r = await llm.getChatCompletion(messages, { temperature });
  return r;
} catch (e) {
  if (e.message.startsWith('Invalid response body returned from OpenRouter'))
    return fallbackResponse(); // upstream 200-with-error; retry or degrade
  throw e;
}

Prevention

When it happens

Trigger: OpenRouter forwards an upstream error as HTTP 200 with an error body (no choices); empty choices array from a content filter; response schema drift; provider returned only usage/metadata with no completion; rate-limit-style body that the SDK treated as success.

Common situations: Upstream provider errored and OpenRouter wrapped it as 200-with-error; content policy produced zero choices; transient provider hiccup; model returns tool-call-only with no text choices; partial outage where OpenRouter returns metadata but no content.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@526360e320 (2026-08-13). Data as JSON: /api/errors/5ad39777a0959d6a. Report an issue: GitHub.