linshenkx/prompt-optimizer · error · APIError

No valid response received

Error message

No valid response received

What it means

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.

Source

Thrown at packages/core/src/services/llm/adapters/openai-adapter.ts:1153

  protected async parseCompletionResponse(response: any, modelId: string): Promise<LLMResponse> {
    // 处理原始 SSE 字符串响应(某些 API 返回未解析的 SSE 格式)
    if (typeof response === 'string') {
      return this.parseSSEResponse(response, modelId)
    }

    // 检测是否为流式响应(某些 API 强制返回流式响应)
    if (this.isStreamResponse(response)) {
      return await this.consumeStreamResponse(response as AsyncIterable<any>, modelId)
    }

    // 处理响应中的 reasoning_content 和普通 content
    if (!response.choices || response.choices.length === 0) {
      throw new APIError('API returned invalid response: choices is empty or missing')
    }

    const choice = response.choices[0]
    if (!choice?.message) {
      throw new APIError('No valid response received')
    }

    let content = choice.message.content || ''
    let reasoning = ''

    // 处理推理内容(如果存在)
    // SiliconFlow 等提供商在 choice.message 中并列提供 reasoning_content 字段
    if ((choice.message as any).reasoning_content) {
      reasoning = (choice.message as any).reasoning_content
    } else {
      // 检测并分离content中的think标签
      const processed = this.processThinkTags(content)
      content = processed.content
      reasoning = processed.reasoning || ''
    }

    return {
      content: content,

View on GitHub (pinned to 3e677b1d9f)

Solutions

  1. Inspect the raw choice object (finish_reason often explains it)
  2. If finish_reason is content_filter, adjust prompt/inputs
  3. Fix or upgrade the compatible server to include a message object
  4. Handle gracefully with a retry or user-facing notice

Example fix

// before
const r = await openai.chat.completions.create({...})
const text = r.choices[0].message.content

// after
const r = await openai.chat.completions.create({...})
const c = r.choices?.[0]
if (!c?.message) throw new Error(`No valid response received (finish_reason=${c?.finish_reason})`)
const text = c.message.content ?? ''
Defensive patterns

Strategy: validation

Validate before calling

null

Type guard

function hasValidMessage(r: unknown): boolean {
  const c = (r as any)?.choices?.[0]
  return !!c && typeof c.message === 'object' && c.message !== null
}

Try / catch

try { return await adapter.sendMessage(msgs) }
catch (e) {
  if (e instanceof APIError && e.message === 'No valid response received') {
    if (lastFinishReason === 'content_filter') return regenerateWithSaferPrompt()
    return retryOnce(() => adapter.sendMessage(msgs))
  }
  throw e
}

Prevention

When it happens

Trigger: Malformed first choice from an OpenAI-compatible server, responses where content is null after content filtering, or gateway bugs dropping the message field.

Common situations: Content-filtered completions (message null with finish_reason:'content_filter'), half-implemented compatible servers, race-y streaming-to-nonstreaming conversions.

Related errors


AI-assisted analysis of linshenkx/prompt-optimizer@3e677b1d9f (2026-08-27). Data as JSON: /api/errors/d301e92e7b9d6545. Report an issue: GitHub.