Skip to content

[Bug] preserveReasoningContentModels still emits bare tool_call continuations when the replay cache misses (long sessions) — DeepSeek thinking mode 400 #1193

Description

@gdxnpy

Client or integration

Codex App (also reproduced with a direct /v1/chat/completions client)

Area

Provider adapter

Summary

The #950 fix re-attaches reasoning_content from the in-process replay cache, but that cache is bounded (64 entries / 256 KiB / 1 h TTL, see src/responses/reasoning-replay-cache.ts). When the cache misses on a long session, src/adapters/openai-chat.ts still emits an assistant tool_call history message without reasoning_content, and DeepSeek thinking mode rejects it with HTTP 400:

Provider error 400: Error from provider (Console): Upstream request failed: [invalid_request_error] The `reasoning_content` in the thinking mode must be passed back to the API.

Both affected paths have no fallback when the cache lookup is empty:

  • Main assistant path (openai-chat.ts ~line 348): if (cached.length > 0) { reasoningContent = ... } — when cached.length === 0, the message goes out bare.
  • Orphan-repair path (openai-chat.ts ~line 412): ...(cachedReasoning ? { reasoning_content: cachedReasoning } : {}) — when cachedReasoning is undefined, the synthesized assistant tool_call has no reasoning_content.

Reproduction

  1. Configure the opencode-zen provider with preserveReasoningContentModels: ["deepseek-v4-flash-free"].
  2. Run a long multi-turn tool-calling session with DeepSeek thinking mode (reasoning_effort=max).
  3. Continue a turn whose assistant tool_call history entry lost its reasoning item (compaction, lost assistant turn, or resumed thread) and whose call id is no longer in the replay cache (evicted by >64 entries, >256 KiB, or >1 h TTL).
  4. The request fails with HTTP 400 as above.

Verified conditions:

  • A/B test with the same conversation shape: assistant tool_call message missing reasoning_content -> 400; same shape with a minimal placeholder (" ") -> 200.
  • Failures are intermittent and correlate exactly with cache misses: they appear only when peekReasoningForCall returns nothing for the call id.
  • Reproduced both after >1 h idle and after enough tool rounds to evict the entry.
  • The same session is stable with providers whose upstream tolerates missing reasoning_content (e.g. Kimi), which is why the bug is provider-specific.

Version

2.10.2 (npm @bitkyc08/opencodex); also confirmed present on current main.

Operating system

macOS (darwin, arm64)

Provider and model

opencode-zen / deepseek-v4-flash-free (OpenCode Zen Console, DeepSeek thinking mode)

Logs or error output

Provider error 400: Error from provider (Console): Upstream request failed: [invalid_request_error] The `reasoning_content` in the thinking mode must be passed back to the API.

Redacted configuration

{
  "providers": {
    "opencode-zen": {
      "baseUrl": "https://opencode.ai/zen/v1",
      "preserveReasoningContentModels": ["deepseek-v4-flash-free"]
    }
  }
}

Suggested fix

  • When the replay cache misses for a preserveReasoningContentModels provider, never emit a bare tool_call continuation: inject a minimal placeholder (" ") in both the main assistant path and the orphan-repair path of src/adapters/openai-chat.ts. Verified locally that DeepSeek thinking mode accepts the placeholder (both " " and "..." returned 200), so this closes the 400 deterministically.
  • Longer-term: instead of relying on the bounded in-process side cache, persist the reasoning attachment with the conversation state (e.g. responses-state) so resumed tool loops keep the original reasoning text, or make cache eviction aware of preserveReasoningContentModels call ids.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingproviderProvider adapters, OpenAI-compat presets, upstream API quirkstoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions