codex exec --json emits duplicate id keys for web-search items

Open Beginner friendly
#60,992 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
84/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
json, python, rust

Research direction

Start in codex-rs/exec/src/exec_events.rs, where the issue points to the outer item and flattened web-search payload definitions. Trace how those fields serialize, then add a raw JSON serialization test that rejects duplicate keys, as requested in the issue. Done means each emitted web-search item has one unambiguous id and the test confirms duplicate keys are rejected.

Written by the indexing model from the issue text.

Description

bug CLI exec

What issue are you seeing?

codex exec --json emits two different id properties in each web-search item. Parsers that keep the first property identify the item as item_1; parsers that keep the last identify it as ws-1. A duplicate-rejecting parser cannot consume the event.

This is separate from #60990, which concerns an omitted TypeScript field.

Reproduction

Run from the repository root with the Python SDK dependencies and codex on PATH. The Responses API is a local mock; no external web request is made.

import json, os, shutil, subprocess, sys, tempfile
from pathlib import Path

sys.path[:0] = [str(Path("sdk/python/src").resolve()),
                str(Path("sdk/python/tests").resolve())]
from app_server_harness import (
    AppServerHarness, ev_assistant_message, ev_completed,
    ev_response_created, sse,
)

def reject_duplicates(pairs):
    result = {}
    for key, value in pairs:
        if key in result:
            raise ValueError(f"duplicate key: {key}")
        result[key] = value
    return result

with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as tmp:
    with AppServerHarness(Path(tmp)) as harness:
        added = {"type": "response.output_item.added", "item": {
            "type": "web_search_call", "id": "ws-1", "status": "in_progress"}}
        done = {"type": "response.output_item.done", "item": {
            "type": "web_search_call", "id": "ws-1", "status": "completed",
            "action": {"type": "search", "query": "example"}}}
        harness.responses.enqueue_sse(sse([
            ev_response_created("r1"), added, done,
            ev_assistant_message("m1", "Finished."), ev_completed("r1"),
        ]))
        env = dict(os.environ, CODEX_HOME=str(harness.codex_home),
                   CODEX_APP_SERVER_DISABLE_MANAGED_CONFIG="1")
        proc = subprocess.run([
            shutil.which("codex"), "exec", "--json", "--skip-git-repo-check",
            "-c", 'web_search="cached"', "-c", "features.plugins=false",
            "-c", "features.plugin_discovery_prefetch=false", "Example",
        ], cwd=harness.workspace, env=env, capture_output=True, text=True,
           check=True, timeout=15)
        for line in proc.stdout.splitlines():
            event = json.loads(line)
            if event.get("item", {}).get("type") == "web_search":
                print(line)
                try:
                    json.loads(line, object_pairs_hook=reject_duplicates)
                except ValueError as error:
                    print(error)

Observed for the started item (the completed item has the same duplication):

{"type":"item.started","item":{"id":"item_1","type":"web_search","id":"ws-1","query":"","action":{"type":"other"}}}
duplicate key: id

Python's default decoder retains ws-1; a first-wins decoder retains item_1. A separate structurally equivalent Serde deserialization test returned duplicate field id.

Expected behavior

Emit one unambiguous id per item. The outer item flattens a web-search payload that also has an id. Remove the duplicate or give the raw search identifier a distinct field name if both IDs are needed. Add a raw-JSON serialization test that rejects duplicate keys.

Tested with Codex CLI 0.167.0-alpha.4.1, Python 3.14.0, and Windows; source checked at 6bfdc75f0d9e6fae313234f694839866d7c63c39.

Dominant language
Rust
Stars
135k
Forks
21.4k
Avg merge
1m
Merged PRs (30d)
995

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from openai/codex

All issues in openai/codex

Similar issues

More Rust issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.