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
- Inspect the embedded JSON — it names the real problem (often a Gitee-side error object that arrived with status 200)
- Retry the message: transient malformed responses usually clear immediately
- Switch to a mainstream chat model id (e.g. a Qwen instruct variant) if the payload looks like a non-chat schema
- 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
- Retry once on malformed payloads — transient gateway responses usually clear
- Log the embedded JSON payload; it usually contains the upstream error object
- Prefer well-supported chat model ids on Gitee AI to avoid non-chat response schemas
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)