{"record":{"id":"b46103d05e151520","repo":"Mintplex-Labs/anything-llm","slug":"invalid-response-body-returned-from-giteeai-jso","errorCode":null,"errorMessage":"Invalid response body returned from GiteeAI: ${JSON.stringify(result.output)}","messagePattern":"Invalid response body returned from GiteeAI: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"server/utils/AiProviders/giteeai/index.js","lineNumber":139,"sourceCode":"\n  async getChatCompletion(messages = null, { temperature = 0.7 }) {\n    const result = await LLMPerformanceMonitor.measureAsyncFunction(\n      this.openai.chat.completions\n        .create({\n          model: this.model,\n          messages,\n          temperature,\n        })\n        .catch((e) => {\n          throw new Error(e.message);\n        })\n    );\n\n    if (\n      !result?.output?.hasOwnProperty(\"choices\") ||\n      result?.output?.choices?.length === 0\n    )\n      throw new Error(\n        `Invalid response body returned from GiteeAI: ${JSON.stringify(result.output)}`\n      );\n\n    return {\n      textResponse: this.#parseReasoningFromResponse(result.output.choices[0]),\n      metrics: {\n        prompt_tokens: result.output.usage.prompt_tokens || 0,\n        completion_tokens: result.output.usage.completion_tokens || 0,\n        total_tokens: result.output.usage.total_tokens || 0,\n        outputTps: result.output.usage.completion_tokens / result.duration,\n        duration: result.duration,\n        model: this.model,\n        provider: this.className,\n        timestamp: new Date(),\n      },\n    };\n  }\n","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/server/utils/AiProviders/giteeai/index.js#L121-L157","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// shape-check the raw response yourself if calling the endpoint directly\nconst body = await response.json();\nconst hasChoices =\n  body && typeof body === \"object\" && Array.isArray(body.choices) && body.choices.length > 0;\nif (!hasChoices) {\n  throw new Error(`Unexpected GiteeAI payload: ${JSON.stringify(body).slice(0, 300)}`);\n}","typeGuard":"function isGiteeChatCompletion(payload) {\n  return (\n    payload != null &&\n    typeof payload === \"object\" &&\n    Array.isArray(payload.choices) &&\n    payload.choices.length > 0 &&\n    payload.choices[0]?.message != null\n  );\n}","tryCatchPattern":"try {\n  return await llm.getChatCompletion(messages);\n} catch (err) {\n  if (/Invalid response body returned from GiteeAI/.test(err.message)) {\n    // the message embeds the raw JSON — log it and retry once; persistent failures mean gateway/model issue\n    log.warn(\"GiteeAI malformed payload:\", err.message);\n    return retryOnceThenGiveUp();\n  }\n  throw err;\n}","preventionTips":["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"],"tags":["gitee-ai","malformed-api-response","response-validation","chat-completions"],"backgroundTag":"malformed-api-response","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}