Skip to content

0.161.0: remote compaction fails after resuming hosted web_search_call history on native OpenAI #51962

Description

@yuanyuanlove

What version of Codex CLI is running?

codex-cli 0.161.0,官方 @openai/codex@0.161.0 的 macOS arm64 二进制。2026-10-08 测试时 npm latest 为此版本。独立安装,没有改动二进制。

What subscription do you have?

ChatGPT subscription authentication(同一现有登录账户,测试使用临时认证副本;未提供账户标识或凭据)。

Which model were you using?

gpt-6.1-sol,reasoning effort high,内置 openai provider。

What platform is your computer?

macOS 26.3.0 / arm64。

What terminal emulator and version are you using (if applicable)?

Python 驱动 codex app-server --stdio 的 JSON-RPC;本报告核心复现不依赖桌面 UI。

Codex doctor report

本轮未运行 doctor。使用独立临时 CODEX_HOME、空工作目录、无自定义 provider、无模型目录覆盖。effective config:

model = "gpt-6.1-sol"
model_provider = "openai"
model_reasoning_effort = "high"
web_search = "live"
approval_policy = "never"
sandbox_mode = "read-only"
project_doc_max_bytes = 0

config/read 显示 openai_base_url=null、model_providers={},chatgpt_base_url=https://chatgpt.com/backend-api/。

What issue are you seeing?

官方 0.161.0 app-server 恢复含真实 hosted web_search_call 的历史后,remote compaction 失败;同一历史仅移除这些搜索项,压缩及后续对话成功。

关键范围:这是通过实验性 thread/resume.history 将真实服务端输出交给官方客户端恢复的兼容性测试,不是声称默认新会话搜索必然失败,也不是桌面 UI 自然切换模型的完整复现。压缩请求由官方 thread/compact/start 构造,不是手写 compaction body。核心 A/B 没有经过 OpenCodex 或其他模型网关。

失败终态:

Error running remote compact task: stream disconnected before completion: stream closed before response.completed

turn/completed.turn.status=failed、codexErrorInfo=other;没有生成 compacted replacement history。过程中先出现过一次 high-demand 提示,随后是 stream-disconnected 重试;不把 high-demand 文案本身当作已确认根因。

2026-10-08 12:36–12:37 UTC+8 的对照:

恢复的历史 结果 耗时
user message + 3 个真实 web_search_call + reasoning + assistant message remote compaction failed 34.49 s
同一历史,仅移除 web_search_call;保留 reasoning 与其他消息 completed;生成非空 compaction.encrypted_content 17.81 s
成功压缩后,询问预设测试事实 completed;正确回答 AZURE-KITE-731, 17 2.40 s

三条 hosted action 分别是 search、open_page、find_in_page,ID 来自真实返回,不是合成 ID。没有修改剩余 reasoning 内容或密文。

另外,官方 0.161.0 的默认全新 Sol 会话,正常搜索→压缩→续聊全部成功。该基线的 rollout 记录搜索为 custom_tool_call(name=exec) + output,而不是 hosted web_search_call;所以它不能覆盖上述旧式 hosted 历史的恢复场景。

What steps can reproduce the bug?

此方法涉及真实模型请求,会产生正常用量;请只使用全新、无敏感信息的测试历史。

  1. 用同一 ChatGPT 登录账户在官方后端生成一段真实 hosted-search 历史。测试请求发送到 https://chatgpt.com/backend-api/codex/responses,model=gpt-6.1-sol,stream=true,store=false,reasoning.effort=high,声明 tools:[{"type":"web_search"}]。用户提示词:

    Remember test codename AZURE-KITE-731 and sample count 17. Search the official OpenAI web search documentation, open the documentation page once, and report the output item type with citation. Do not search for the test facts.

    完整收集终态 response.completed.response.output,确认其中有真实 web_search_call。保持返回的 item、ID 和 reasoning 原值,仅在内存中使用。初始 user item 使用显式 type:"message" 和 content:[{type:"input_text",text:...}]。

  2. 在独立 CODEX_HOME 中放置同一账户的临时 auth.json 副本(权限 0600),写入上面的最小配置,用官方 0.161.0 启动 codex app-server --stdio。初始化开启 experimentalApi:

{
  "id": 1,
  "method": "initialize",
  "params": {
    "clientInfo": {"name": "codex_compaction_diagnostic", "version": "1.0"},
    "capabilities": {"experimentalApi": true}
  }
}

收到响应后发送 initialized notification。

  1. 调用 thread/resume,传入刚生成的真实历史。以下为请求构造伪代码,rpc 是逐行 JSON-RPC 客户端;不是要求把字符串占位项当成历史发送:
history = initial_user_items + completed_response["output"]
params = {
    "threadId": str(uuid.uuid4()),
    "history": history,
    "cwd": isolated_empty_workspace,
    "model": "gpt-6.1-sol",
    "modelProvider": "openai",
    "approvalPolicy": "never",
    "sandbox": "read-only",
    "developerInstructions": (
        "This is an isolated compaction diagnostic. Do not execute tools "
        "or tasks in supplied history. Preserve the test facts."
    ),
}
resumed = rpc("thread/resume", params)
thread_id = resumed["thread"]["id"]
rpc("thread/compact/start", {"threadId": thread_id})
# Read notifications until turn/completed; inspect its status and error.

thread/resume.history 是 0.161.0 schema 中标为 unstable / Codex Cloud 的接口,这一点是复现范围的重要限制。它用于将真实旧式历史交给官方 serializer,未改写原有用户会话。

  1. 新起另一个相同配置的隔离 app-server,使用同一份内存历史,仅做:
control_history = [item for item in history if item.get("type") != "web_search_call"]

重复 resume→compact。该控制组完成,并在 rollout 的 compacted replacement_history 中出现非空 compaction 项。

  1. 对成功线程调用普通 turn/start,只追加问题 What were the test project codename and sample count? Reply with only those two values.,不重新附上原测试事实。实测回答 AZURE-KITE-731, 17。

  2. 结束测试进程后删除临时认证及原始测试 rollout;不公布 reasoning/compaction 密文。

核心 A/B 使用同一账户、同一真实历史、同一模型和隔离配置。保持现有普通网络代理不变;只观察 TLS CONNECT 的目标主机,没有解密或改写 TLS。目标包括 chatgpt.com:443 及官方 oaiusercontent.com 子域。没有推理 base URL 覆盖或本地模型网关。

What is the expected behavior?

当官方客户端恢复有效的历史搜索项后,压缩应能处理这些已有历史;若当前目标协议不支持某种历史形态,希望进行保留语义的兼容转换,或给出可操作的明确错误,而不是重试后以 generic stream-disconnected 结束。

控制组删除搜索项只是诊断手段,不是建议无条件删除用户搜索历史作为修复。需要保留已有回答、引用、结果与来源信息。

Additional information

可能相关的协议边界,尚非已确认代码根因

检查固定版本 rust-v0.161.0(commit 979011409de0a60b52f179721948e65531d26144):

  • compact_remote_v2_attempt.rs 的压缩 Prompt 使用当前 tool router 的声明。
  • client.rs 在 Responses Lite 中使用 additional_tools,而普通 Responses 使用顶层 tools。
  • spec_plan.rs 对 Lite 不声明 hosted WebSearch;该模型默认新搜索走 standalone web.run/code mode。
  • history.rs 接受历史 WebSearchCall。

因此怀疑是 旧 hosted 历史与恢复后当前工具/协议表示之间的兼容边界。尚未捕获官方压缩请求的最终解密出站 body,不能认定具体错误发生在哪行客户端或后端代码。

独立的手写 API 对照也表明:同一真实 hosted 历史,带 tools:[{type:"web_search"}] 时压缩完成;省略该声明则失败;带声明时删除 item ID 仍可完成。因此没有证据把 ID 清理或 reasoning 密文大小当作该样本根因。这些 API 对照不是官方客户端的等价请求,只作为定位线索。

相关 issue 与未覆盖范围

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    app-serverIssues involving app server protocol or interfaces
    contextIssues related to context management (including compaction)
    on Oct 8, 2026
  2. github-actions commented on Oct 8, 2026

    @github-actions
    Contributor

    English translation:

    What version of Codex CLI is running?

    codex-cli 0.161.0, the official macOS arm64 binary from @openai/codex@0.161.0. This was the npm latest version when tested on 2026-10-08. Installed independently, with no modifications to the binary.

    What subscription do you have?

    ChatGPT subscription authentication (the same existing signed-in account, using a temporary authentication copy for testing; no account identifiers or credentials are provided).

    Which model were you using?

    gpt-6.1-sol, reasoning effort high, built-in openai provider.

    What platform is your computer?

    macOS 26.3.0 / arm64.

    What terminal emulator and version are you using (if applicable)?

    JSON-RPC with Python driving codex app-server --stdio; the core reproduction in this report does not depend on the desktop UI.

    Codex doctor report

    Doctor was not run during this round. Used a separate temporary CODEX_HOME, an empty working directory, no custom provider, and no model catalog override. Effective config:

    model = "gpt-6.1-sol"
    model_provider = "openai"
    model_reasoning_effort = "high"
    web_search = "live"
    approval_policy = "never"
    sandbox_mode = "read-only"
    project_doc_max_bytes = 0

    config/read showed openai_base_url=null, model_providers={}, and chatgpt_base_url=https://chatgpt.com/backend-api/.

    What issue are you seeing?

    After the official 0.161.0 app-server resumes history containing real hosted web_search_call items, remote compaction fails; removing only those search items from the same history allows compaction and the subsequent conversation to succeed.

    Scope clarification: This is a compatibility test that passes real server output to the official client for resumption through the experimental thread/resume.history API. It does not claim that searches in default new sessions necessarily fail, nor is it a complete reproduction of a natural model switch in the desktop UI. The compaction request is constructed by the official thread/compact/start, rather than a manually written compaction body. The core A/B test did not go through OpenCodex or any other model gateway.

    Final failure state:

    Error running remote compact task: stream disconnected before completion: stream closed before response.completed
    

    turn/completed.turn.status=failed, codexErrorInfo=other; no compacted replacement history was generated. A high-demand message appeared once during the process, followed by stream-disconnected retries; the high-demand wording itself is not treated as a confirmed root cause.

    Comparison from 2026-10-08 12:36–12:37 UTC+8:

    Resumed history Result Duration
    user message + 3 real web_search_call items + reasoning + assistant message remote compaction failed 34.49 s
    Same history, with only web_search_call removed; reasoning and other messages retained completed; generated nonempty compaction.encrypted_content 17.81 s
    After successful compaction, asked about the predefined test facts completed; correctly answered AZURE-KITE-731, 17 2.40 s

    The three hosted actions were search, open_page, and find_in_page; their IDs came from real responses and were not synthetic IDs. The remaining reasoning content and ciphertext were not modified.

    In addition, a default fresh Sol session in official 0.161.0 successfully completed a normal search→compaction→continued conversation sequence. The rollout for that baseline recorded the search as custom_tool_call(name=exec) + output, rather than hosted web_search_call; therefore, it does not cover the resumption scenario involving legacy hosted history described above.

    What steps can reproduce the bug?

    This method involves real model requests and incurs normal usage; please use only fresh test history containing no sensitive information.

    1. Generate real hosted-search history on the official backend using the same ChatGPT signed-in account. Send the test request to https://chatgpt.com/backend-api/codex/responses, with model=gpt-6.1-sol, stream=true, store=false, reasoning.effort=high, and declare tools:[{"type":"web_search"}]. User prompt:

      Remember test codename AZURE-KITE-731 and sample count 17. Search the official OpenAI web search documentation, open the documentation page once, and report the output item type with citation. Do not search for the test facts.

      Collect the complete final response.completed.response.output and confirm that it contains real web_search_call items. Preserve the returned items, IDs, and reasoning exactly as returned, and use them only in memory. The initial user item uses explicit type:"message" and content:[{type:"input_text",text:...}].

    2. Place a temporary copy of the same account's auth.json in a separate CODEX_HOME (permissions 0600), write the minimal configuration above, and start codex app-server --stdio using official 0.161.0. Enable experimentalApi during initialization:

    {
      "id": 1,
      "method": "initialize",
      "params": {
        "clientInfo": {"name": "codex_compaction_diagnostic", "version": "1.0"},
        "capabilities": {"experimentalApi": true}
      }
    }

    After receiving the response, send an initialized notification.

    1. Call thread/resume, passing the real history just generated. The following is pseudocode for constructing the request; rpc is a line-based JSON-RPC client. It is not an instruction to send string placeholders as history:
    history = initial_user_items + completed_response["output"]
    params = {
        "threadId": str(uuid.uuid4()),
        "history": history,
        "cwd": isolated_empty_workspace,
        "model": "gpt-6.1-sol",
        "modelProvider": "openai",
        "approvalPolicy": "never",
        "sandbox": "read-only",
        "developerInstructions": (
            "This is an isolated compaction diagnostic. Do not execute tools "
            "or tasks in supplied history. Preserve the test facts."
        ),
    }
    resumed = rpc("thread/resume", params)
    thread_id = resumed["thread"]["id"]
    rpc("thread/compact/start", {"threadId": thread_id})
    # Read notifications until turn/completed; inspect its status and error.

    thread/resume.history is an interface marked unstable / Codex Cloud in the 0.161.0 schema, which is an important limitation of the reproduction scope. It is used to pass real legacy history to the official serializer; no existing user session was rewritten.

    1. Start another isolated app-server with the same configuration, using the same in-memory history, making only this change:
    control_history = [item for item in history if item.get("type") != "web_search_call"]

    Repeat resume→compact. This control group completes, and a nonempty compaction item appears in the rollout's compacted replacement_history.

    1. Call a normal turn/start on the successful thread, appending only the question What were the test project codename and sample count? Reply with only those two values., without reattaching the original test facts. The observed answer was AZURE-KITE-731, 17.

    2. After terminating the test processes, delete the temporary authentication files and raw test rollouts; do not publish reasoning/compaction ciphertext.

    The core A/B test used the same account, the same real history, the same model, and the same isolated configuration. The existing standard network proxy was kept unchanged; only the destination hosts of TLS CONNECT were observed, without decrypting or modifying TLS. Destinations included chatgpt.com:443 and official oaiusercontent.com subdomains. There was no inference base URL override or local model gateway.

    What is the expected behavior?

    After the official client resumes valid historical search items, compaction should be able to handle that existing history. If the current target protocol does not support a particular history format, it should perform a compatibility conversion that preserves semantics, or provide a clear, actionable error, rather than ending with a generic stream-disconnected error after retries.

    Removing search items in the control group is only a diagnostic measure, not a recommendation to unconditionally delete users' search history as a fix. Existing answers, citations, results, and source information need to be preserved.

    Additional information

    Potentially relevant protocol boundary, not yet a confirmed root cause in the code

    Inspected the pinned version rust-v0.161.0 (commit 979011409de0a60b52f179721948e65531d26144):

    • The compaction Prompt in compact_remote_v2_attempt.rs uses declarations from the current tool router.
    • client.rs uses additional_tools in Responses Lite, while ordinary Responses uses top-level tools.
    • spec_plan.rs does not declare hosted WebSearch for Lite; new searches with this model use standalone web.run/code mode by default.
    • history.rs accepts historical WebSearchCall items.

    This suggests a compatibility boundary between legacy hosted history and the current tool/protocol representation after resumption. The final decrypted outbound body of the official compaction request has not been captured, so it is not possible to identify the specific line of client or backend code where the error occurs.

    Separate manually written API controls also showed that, with the same real hosted history, compaction completes when tools:[{type:"web_search"}] is included, but fails when that declaration is omitted; when the declaration is included, removing item IDs still allows completion. Therefore, there is no evidence that ID cleanup or reasoning ciphertext size is the root cause for this sample. These API controls are not equivalent requests to those made by the official client and serve only as diagnostic clues.

    Related issues and scope not covered

  3. Nakagawa-master commented on Oct 8, 2026

    @Nakagawa-master

    The A/B here is unusually useful because it isolates a historical tool-schema compatibility boundary without claiming a normal fresh-session failure. One additional invariant may help turn this into a safe, narrow fix:

    A historical tool call is evidence of a past operation, not an authorization to offer that tool for new operations. Restoring/compacting a legacy hosted web_search_call may require a representation understood by the compaction backend, but that need must not silently expand the resumed session's current tool set.

    I checked current Codex source alongside the reporter's 0.161.0 pin:

    • codex-rs/core/src/compact_remote_v2_attempt.rs builds a compaction prompt from history and normally supplies step_context.tool_router.model_visible_specs(); its special tools_in_history case is keyed to Responses Lite history with AdditionalTools. That is not visibly a compatibility rule for legacy hosted WebSearchCall, though this source alone does not establish which compact request path the 0.161.0 repro used.
    • codex-rs/core/src/client.rs separately formats tool declarations for Responses Lite vs ordinary Responses. The reporter's own independent API control — old hosted history passes with a historical web_search declaration and fails without it — makes history-item type × destination request tool-schema a sharper test axis than retry timing, ID cleanup, or encrypted reasoning size. The API experiment is a clue, not an assertion about the unobserved official request body.

    Implementation decision to verify: distinguish (1) historical decoding/validation capability required to read web_search_call items at compaction time from (2) currently executable tool authority exposed to an agent turn. If the target protocol requires a tool declaration to accept a past hosted item, provide the smallest compatible compaction-only schema or validated semantic normalization; do not change the active router or permit a fresh hosted search solely because an old call appears in history. Preserve provenance: original call/result association, citation/source references, and prior answer meaning. If the endpoint cannot represent that history type, return an explicit unsupported-history diagnostic rather than discarding it or presenting an opaque stream disconnect.

    A bounded regression can use the existing sanitized real history and control:

    1. Legacy web_search_call history imported via thread/resume.history → compact → ask about the preexisting test facts: succeeds without dropping history, with historical references retained.
    2. Same history, original live tool registry still lacks hosted web_search: compaction compatibility does not make web_search newly callable on a subsequent ordinary turn.
    3. Existing modern custom_tool_call(name=exec) search history → compact → continue: unchanged behavior.
    4. Legacy hosted history with a missing or incompatible companion reference: either a validated non-lossy conversion or a typed, actionable failure; never a silently invented citation/result.
    5. Inspect the official compaction-request representation just before dispatch (item-type/tool-schema inventory only, no reasoning contents or private transcript) to discriminate unsupported request shape from a transport/server-side disconnect.

    This is a proposed causal boundary and acceptance test, not a reproduction by me or proof that the remote-v2 code path is the culprit. The underlying distinction also complements the earlier provenance/authority boundary, without implying any existing Codex implementation was derived from it. One confirming maintainer-side request-shape trace or a failing regression would settle which compatibility layer should own the fix.

  4. philkunz commented on Oct 9, 2026

    @philkunz

    Additional evidence: the same failure occurring naturally (no thread/resume.history import) on 0.162.0 / Linux, in the local compaction path, with the outbound compaction request and the upstream error captured.

    Setup

    • codex-cli 0.162.0 (rust-v0.162.0, c1382380de), standalone x86_64-unknown-linux-musl build, long-running threads on a shared codex app-server daemon.
    • gpt-6.1-sol, reasoning effort xhigh, ChatGPT subscription auth, web_search = "live".
    • A custom model_provider: a pass-through Responses proxy that forwards request bodies and tool declarations unchanged, with no capability overrides. As a result, Codex uses local summarization compaction (core/src/compact.rs), not remote compaction v2. The bug therefore isn't specific to remote v2 or to imported history.

    What happens

    A/B: the persisted history of one affected thread, replayed outside Codex through the same pass-through path (389 items, about 1.27 MB)

    Request Result
    unchanged history, tools: [] fails: the 143-byte internal_error above
    unchanged history, only tools: [{"type":"web_search"}] added completes (215,617 input tokens)
    only the single web_search_call removed, tools: [] completes (211,133 input tokens)
    every reasoning item removed, web_search_call kept, tools: [] still fails, same error

    Size is not the cause: a fresh thread on the same account, path and config, without hosted-search history, completed a 1,583,136-byte / 271,441-token request. On the next turn it auto-compacted successfully (local compaction at 257,801 input tokens), then continued at 39,856.

    Where it seems to come from (0.162.0): core/src/compact.rs (around line 304) builds the compaction Prompt with default (empty) tools, while the model-visible history it sends still contains the hosted web_search_call. The backend apparently requires a matching hosted-tool declaration for that history item. It answers with internal_error rather than a 4xx validation error, which also explains #52016 / #52043 ("response protection is unavailable").

    Suggested fix shape (agreeing with the invariant in the comment above): when the history contains hosted tool calls, the compaction request, both local and remote v2, should carry the matching declaration as a compaction-only schema, without widening the live turn's tool authority. Separately, an SSE error event, or a response.failed, should be reported as such, with its type and message, instead of as a missing response.completed.

    No conversation content, reasoning ciphertext or credentials are included above; the counts come from item-type inventories only.

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

    CLIIssues related to the Codex CLIapp-serverIssues involving app server protocol or interfacesbugSomething isn't workingcontextIssues related to context management (including compaction)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions