Skip to content

[Bug]: Forced web_search termination drops synthetic tool declaration, causing GLM/开源模型 to emit raw <tool_call>web_search</tool_call> as content #6464

Closed

Description

@nullburn

Environment

  • OpenCodex version: 2.73.0 (macOS, global proxy on port 10100)
  • Upstream provider: adapter: openai-chat with spark-vllm (GLM-5.3-Flash on TensorFold/vLLM)
  • Client: Codex Desktop (Responses API)

Describe the Bug

In src/web-search/run-turn-loop.ts, when searchesExecuted >= plan.maxSearches (default 1), OpenCodex removes web_search from context.tools on the subsequent iteration:

const nextForceAnswer = searchesExecuted >= plan.maxSearches;
const iterParsed: OcxParsedRequest = {
  ...parsed,
  context: {
    ...parsed.context,
    messages: nextForceAnswer && executedSearchCount > 0
      ? [...messages, forcedAnswerNudge()]
      : messages,
    tools: nextForceAnswer ? toolsNoWebSearch : allTools,
  },
};

When calling reasoning-heavy open-source models (such as GLM-5.3-Flash), the model may decide in its reasoning that the first search result requires follow-up verification and attempts a second search. Because web_search was abruptly stripped from tools, the upstream inference server (TensorFold / vLLM) no longer recognizes web_search as a declared tool.

Depending on the backend:

  1. The backend refuses to parse the undeclared tool into tool_calls and leaks the raw markup into message.content:
    <tool_call>web_search<arg_key>queries</arg_key><arg_value>[...]</arg_value></tool_call>
  2. OpenCodex does not intercept this raw XML block in the openai-chat adapter.
  3. Codex Desktop receives the text, sees the model finish, and ends the turn with phase: final_answer, leaving the session broken and unfinished.

Suggested Fix

  1. In run-turn-loop.ts, allow configuring maxSearches (e.g., 2-3 searches per turn), or keep the tool declared with a graceful fallback instead of immediately stripping it.
  2. In the openai-chat adapter (serialized-tool-call-content.ts), add GLM-style tag parsing (<tool_call>name<arg_key>...</tool_call>) alongside the existing <function=name> parser, so that undeclared or leaked tool calls can be intercepted before reaching Codex.

Activity

  1. github-actions commented on Oct 2, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  2. added
    bugSomething isn't working
    toolstool_calls, MCP, web-search / sidecar tools
    on Oct 2, 2026
  3. Ingwannu commented on Oct 3, 2026

    @Ingwannu
    Owner

    The declaration-removal path is still present on trusted dev 4b74668: run-turn-loop.ts lines 543-551 chooses toolsNoWebSearch once the search budget is exhausted. #6470 addresses that narrow path in both loops; I read its current four-file diff at ea51599. Keep this issue open pending review, current-head CI and dev landing. This source confirmation is not a live GLM reproduction or proof that every raw tag leak is caused by declaration removal. The alternate GLM parser proposal is separate: recovery must not interpret an undeclared tool name as permission to execute it. The search cap, physical-send/output commitment, cancellation and iteration ceiling still need regression coverage; allowing the declaration must not mean unlimited searches or upstream resends.

  4. added a commit that references this issue on Oct 3, 2026
    a85a437
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