{"record":{"id":"d301e92e7b9d6545","repo":"linshenkx/prompt-optimizer","slug":"no-valid-response-received","errorCode":null,"errorMessage":"No valid response received","messagePattern":"No valid response received","errorType":"exception","errorClass":"APIError","httpStatus":null,"severity":"error","filePath":"packages/core/src/services/llm/adapters/openai-adapter.ts","lineNumber":1153,"sourceCode":"  protected async parseCompletionResponse(response: any, modelId: string): Promise<LLMResponse> {\n    // 处理原始 SSE 字符串响应（某些 API 返回未解析的 SSE 格式）\n    if (typeof response === 'string') {\n      return this.parseSSEResponse(response, modelId)\n    }\n\n    // 检测是否为流式响应（某些 API 强制返回流式响应）\n    if (this.isStreamResponse(response)) {\n      return await this.consumeStreamResponse(response as AsyncIterable<any>, modelId)\n    }\n\n    // 处理响应中的 reasoning_content 和普通 content\n    if (!response.choices || response.choices.length === 0) {\n      throw new APIError('API returned invalid response: choices is empty or missing')\n    }\n\n    const choice = response.choices[0]\n    if (!choice?.message) {\n      throw new APIError('No valid response received')\n    }\n\n    let content = choice.message.content || ''\n    let reasoning = ''\n\n    // 处理推理内容（如果存在）\n    // SiliconFlow 等提供商在 choice.message 中并列提供 reasoning_content 字段\n    if ((choice.message as any).reasoning_content) {\n      reasoning = (choice.message as any).reasoning_content\n    } else {\n      // 检测并分离content中的think标签\n      const processed = this.processThinkTags(content)\n      content = processed.content\n      reasoning = processed.reasoning || ''\n    }\n\n    return {\n      content: content,","sourceCodeStart":1135,"sourceCodeEnd":1171,"githubUrl":"https://github.com/linshenkx/prompt-optimizer/blob/3e677b1d9f7e0493c142c175560531e7ae786dce/packages/core/src/services/llm/adapters/openai-adapter.ts#L1135-L1171","documentation":"The choices array existed but choices[0].message is null/undefined — e.g. the provider returned a choice with only finish_reason or a tool_call envelope without a message object — so the adapter rejects the response as invalid.","triggerScenarios":"Malformed first choice from an OpenAI-compatible server, responses where content is null after content filtering, or gateway bugs dropping the message field.","commonSituations":"Content-filtered completions (message null with finish_reason:'content_filter'), half-implemented compatible servers, race-y streaming-to-nonstreaming conversions.","solutions":["Inspect the raw choice object (finish_reason often explains it)","If finish_reason is content_filter, adjust prompt/inputs","Fix or upgrade the compatible server to include a message object","Handle gracefully with a retry or user-facing notice"],"exampleFix":"// before\nconst r = await openai.chat.completions.create({...})\nconst text = r.choices[0].message.content\n\n// after\nconst r = await openai.chat.completions.create({...})\nconst c = r.choices?.[0]\nif (!c?.message) throw new Error(`No valid response received (finish_reason=${c?.finish_reason})`)\nconst text = c.message.content ?? ''","handlingStrategy":"validation","validationCode":"null","typeGuard":"function hasValidMessage(r: unknown): boolean {\n  const c = (r as any)?.choices?.[0]\n  return !!c && typeof c.message === 'object' && c.message !== null\n}","tryCatchPattern":"try { return await adapter.sendMessage(msgs) }\ncatch (e) {\n  if (e instanceof APIError && e.message === 'No valid response received') {\n    if (lastFinishReason === 'content_filter') return regenerateWithSaferPrompt()\n    return retryOnce(() => adapter.sendMessage(msgs))\n  }\n  throw e\n}","preventionTips":["Inspect finish_reason on filtered/empty completions","Upgrade gateways that drop the message field","Handle null content from content filters explicitly"],"tags":["openai","chat-completions","response-format","content-filter"],"backgroundTag":"unexpected-api-response-shape","analyzedSha":"3e677b1d9f7e0493c142c175560531e7ae786dce","analyzedAt":"2026-08-27T21:29:16.709Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}