Skip to content

[Bug]: an empty forced-answer pass fails the whole web-search turn instead of retrying once #4314

Description

@cortes-ventures

Client or integration

Codex App

Area

Tools / MCP / web search

Summary

A hosted web-search turn aborts with stream disconnected before completion: forced-answer pass produced no usable assistant output when the forced-answer pass ends with a terminal done, no visible assistant text, and no tool call.

#1001 fixed the original silent-success defect by turning that case into a hard LoopError(502, …) at src/web-search/loop.ts:853, and the merged fix (PR #1030) took the second of the two options the report offered — "surface an explicit in-stream error". The first option, "recover with a bounded final-answer retry", was never implemented. As a result one empty final pass ends the entire turn, even though every search result needed to answer is already in the request and nothing about the response is structurally broken.

Expected: when the forced-answer pass produces no usable output and the response is not malformed, re-ask once with the results already gathered; fail the turn only if that retry is also empty. Malformed tool calls should keep failing immediately, because they report an upstream protocol problem.

The upstream model in the observed failure is deepseek-v4.1-flash over the OpenCode Go subscription. An occasionally empty assistant message on a tool-free request is upstream behavior, not a protocol violation, and it is recoverable — the search results are already in context.

Reproduction

  1. ocx start with hosted web search and Codex App connected to the proxy.
  2. Route a model that intermittently returns an empty assistant message for a tool-free request (observed: deepseek-v4.1-flash via the OpenCode Go subscription).
  3. Ask a question that consumes the search budget (default maxSearches=3), so the loop sends the forced-answer request with the synthetic web_search tool removed.
  4. The forced pass returns HTTP 200, a terminal done, no assistant text, and no tool call.
  5. The stream disconnects before completion with the message above and the gathered results are discarded.

Deterministic reproduction on dev at 7a0513c (2.52.0) with the existing runWithWebSearch harness:

  • Pass 1 emits a batched web_search call, consuming the budget.
  • The forced pass emits done with no text_delta and no tool call.

Current dev replays the empty passthrough and then throws:

throw new LoopError(502, "forced-answer pass produced no usable assistant output");

Version

@bitkyc08/opencodex 2.51.0 installed; the defect is present on dev at 7a0513c (2.52.0).

Operating system

macOS 26.6.2 arm64; client is Codex App.

Provider and model

opencode-go (Console Go) subscription route to deepseek-v4.1-flash, adapter openai-chat. The failing request is the forced-answer pass of the hosted web-search loop.

Logs or error output

stream disconnected before completion: forced-answer pass produced no usable assistant output

Code path: src/web-search/loop.ts:853 (LoopError(502, …) inside the forceAnswer terminal check).

Screenshots and supporting files

A fix with regression coverage is linked below as a pull request.

Checks

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions