Mintplex-Labs/anything-llm · error · Error

Invalid response body returned from GiteeAI

Error message

Invalid response body returned from GiteeAI: ${JSON.stringify(result.output)}

What it means

After a successful HTTP call, GiteeAILLM.getChatCompletion checks that result.output has a non-empty choices array and throws this error embedding the raw JSON otherwise. Note the check uses || not &&: it throws when choices is missing OR when choices.length === 0 — and also dereferences result.output.usage right after, so a usage-less body can still crash. Unlike the passthrough catches, this is AnythingLLM asserting the response shape it promised the caller (textResponse + metrics).

Solutions

  1. Inspect the embedded JSON — it names the real problem (often a Gitee-side error object that arrived with status 200)
  2. Retry the message: transient malformed responses usually clear immediately
  3. Switch to a mainstream chat model id (e.g. a Qwen instruct variant) if the payload looks like a non-chat schema
  4. If it persists, check Gitee AI status/docs for the model's response format changes
Defensive patterns

Strategy: validation

Validate before calling

// shape-check the raw response yourself if calling the endpoint directly
const body = await response.json();
const hasChoices =
  body && typeof body === "object" && Array.isArray(body.choices) && body.choices.length > 0;
if (!hasChoices) {
  throw new Error(`Unexpected GiteeAI payload: ${JSON.stringify(body).slice(0, 300)}`);
}

Type guard

function isGiteeChatCompletion(payload) {
  return (
    payload != null &&
    typeof payload === "object" &&
    Array.isArray(payload.choices) &&
    payload.choices.length > 0 &&
    payload.choices[0]?.message != null
  );
}

Try / catch

try {
  return await llm.getChatCompletion(messages);
} catch (err) {
  if (/Invalid response body returned from GiteeAI/.test(err.message)) {
    // the message embeds the raw JSON — log it and retry once; persistent failures mean gateway/model issue
    log.warn("GiteeAI malformed payload:", err.message);
    return retryOnceThenGiveUp();
  }
  throw err;
}

Prevention

When it happens

Trigger: Gitee AI returning 200 with an unexpected body: an error object instead of choices (some gateways do this), a moderation/empty completion, a truncated proxy response, or a model that answers with an embedding-style body. JSON.stringify(result.output) in the message shows exactly what came back.

Common situations: Gitee AI gateway hiccup or partial outage returning malformed payloads; model id routed to a non-chat endpoint; corporate proxy rewriting the response; occasional empty-completion responses from smaller models under load.

Related errors


AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18). Data as JSON: /api/errors/b46103d05e151520. Report an issue: GitHub.

Appendix: source

Thrown at server/utils/AiProviders/giteeai/index.js:139

  async getChatCompletion(messages = null, { temperature = 0.7 }) {
    const result = await LLMPerformanceMonitor.measureAsyncFunction(
      this.openai.chat.completions
        .create({
          model: this.model,
          messages,
          temperature,
        })
        .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 GiteeAI: ${JSON.stringify(result.output)}`
      );

    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 3aec848f28)