Skip to content

codex exec --ephemeral fails to spawn a custom subagent with no thread with id; non-ephemeral succeeds after global agent placement #41474

Closed as duplicate

Description

@ltdlittle-sys

Summary

I am seeing a custom-subagent spawn failure in Codex CLI on Windows. The failure is deterministic in --ephemeral mode and reports:

ERROR codex_core::tools::router: error=collab spawn failed: no thread with id: <root-session-id>

The message resembles the thread-lifecycle symptom described in #35781, but my reproduction uses a user-requested custom agent rather than a memory consolidation agent.

Environment

  • Codex CLI: codex-cli 0.150.0-alpha.8
  • OS: Windows 10 Home, 10.0.19045, x64
  • Model: gpt-5.6-terra
  • Command mode: codex exec --strict-config --skip-git-repo-check
  • The diagnostic prompt forbids networking, external services, secret access, writes, and deletion.

Reproduction

  1. Define chief-secretary.toml and researcher.toml with name, description, developer_instructions, and sandbox_mode = "read-only".
  2. Ask the root session to delegate once to chief-secretary, which is instructed to delegate once to researcher.
  3. Run the command in each mode below.

Project-scoped agents only (<project>/.codex/agents/)

  • Normal mode: failed previously with collab spawn failed: no thread with id: <root-session-id>.
  • --ephemeral: failed previously with the same error.

Global agents also present (~/.codex/agents/)

  • Normal mode: completed with final output responded; no no thread with id router error appeared. The CLI output did not expose a child-agent identity, so this only confirms the requested diagnostic completed without the router error.
  • --ephemeral: failed with the same error, before the delegated agent started.

Example failing invocation:

codex exec --strict-config --skip-git-repo-check --ephemeral -C <project> "<read-only prompt requesting chief-secretary -> researcher delegation>"

Expected behavior

The custom subagent should start in both normal and ephemeral modes, or ephemeral mode should reject the workflow before it exposes a spawn tool that cannot resolve the caller thread.

Actual behavior

Non-ephemeral behavior changes when the agent definition is globally available, but ephemeral mode still fails with a lookup for its own root session ID.

Request

Could this be related to the thread-registration or teardown path described in #35781? If not, please advise which redacted diagnostic fields or rollout details would help isolate the custom-agent/ephemeral path.

Activity

  1. added
    bugSomething isn't working
    CLIIssues related to the Codex CLI
    windows-osIssues related to Codex on Windows systems
    execIssues related to the `codex exec` subcommand
    subagentIssues involving subagents or multi-agent features
    sessionIssues involving session (thread) management, resuming, forking, naming, archiving
    on Aug 29, 2026
  2. interconnectedMe commented on Aug 29, 2026

    @interconnectedMe

    Independent Linux reproduction on CLI 0.151.0-alpha.7.1

    I reproduced the same missing-root-thread error using the newest marketplace Codex IDE extension in an isolated installation.

    Environment:

    • Linux x86_64
    • Codex IDE extension: 26.825.41651
    • Bundled runtime: codex-cli 0.151.0-alpha.7.1
    • Parent: ChatGPT/OpenAI, gpt-5.6-sol
    • Custom role stored globally under ~/.codex/agents/
    • Command mode: codex exec --ephemeral
    • Features: multi_agent=true, multi_agent_v2=true
    • Sandbox: read-only

    The prompt explicitly requested exactly one named custom subagent. V2 failed before the child started:

    collab spawn failed: no thread with id: 01a04c3e-7c00-70d2-a063-80815a38af83
    

    That ID was the root session ID printed in the banner for the same run.

    A useful A/B detail: the otherwise identical ephemeral invocation with V2 disabled got past collaboration routing and reached child startup. It then failed for the separate custom-provider inheritance regression tracked in #17598. Therefore, in this test the no thread with id failure was specific to the V2 ephemeral path rather than custom-role discovery.

  3. xgro commented on Sep 4, 2026

    @xgro

    Root-cause analysis (current stable and current main)

    I investigated this beyond the original custom-agent placement hypothesis. The custom agent definition is not causal: the same failure can be reproduced without any custom role.

    Confirmed environments

    • macOS arm64, official stable codex-cli 0.153.2: reproduced
    • macOS arm64, official stable codex-cli 0.153.1: reproduced
    • current origin/main at a7ab2d66d781b903cb060288a89e26e8d2b9a05f: reproduced with a local app-server integration test

    Together with the Windows and Linux reports already in this issue, the symptom is cross-platform. I have not runtime-tested every intervening release, so I am not claiming that every version in that range is affected.

    Root cause

    This is not a race where the root thread is missing from the in-memory ThreadManager.

    Temporary diagnostic instrumentation showed that, at the failing point:

    • the spawn handler and root use the same ThreadManagerState;
    • the root ID is present in the manager;
    • state.get_thread(parent_thread_id) succeeds.

    The failure occurs after that lookup. For a full-history fork, spawn_forked_thread obtains the live parent and then calls load_agent_model_context. That function unconditionally reconstructs the parent context from ThreadStore, through either read_stored_thread or load_latest_model_context.

    However, an ephemeral session intentionally has no LiveThread persistence handle. Therefore the parent exists in memory but has no persisted thread row/history, and the store lookup returns NoThread. The reported ID is the live root's ID, but no thread with id is describing absence from persistence, not absence from collaboration registration.

    Affected paths

    In MultiAgent V2, omitted fork_turns defaults to all. Consequently:

    • V2 + ephemeral + omitted fork_turns: affected (the default path)
    • V2 + ephemeral + fork_turns: "all": affected
    • V2 + ephemeral + a positive integer: affected
    • V2 + ephemeral + fork_turns: "none": control path; it bypasses history forking and can start a fresh child
    • V1: affected only when context forking is explicitly requested; its fresh-spawn default does not use this path
    • nested delegation: an ephemeral child/parent can hit the same failure when it requests inherited history

    Impact

    The primary impact is execution correctness: the child never starts, so requested delegation or parallel work is skipped or fails.

    There is also a fail-open observability concern: a parent can still finish and codex exec can exit 0, even though the requested child failed. In my CLI reproduction the overall command exited successfully. The broader structured-error/slot-accounting behavior appears related to #33672; I did not confirm a slot leak here, so I would keep that as a separate concern rather than attributing it to this root cause.

    I found no indication that this is an OpenAI service, model, authentication, or subscription dependency. The current-main reproduction uses a local mock Responses server, and the failure happens before a child model request is made. Model selection can influence whether V2 is selected, but the failing lookup itself is local.

    Diagnostic patch and regression proof

    As a proof of the root cause, I tried a narrow experimental fallback: for an ephemeral parent only, build the child's history from the live ContextManager instead of ThreadStore; retain the existing persisted path for non-ephemeral parents.

    I added an app-server regression test that:

    1. creates an ephemeral V2 root;
    2. has the mock parent call spawn_agent with fork_turns: "all";
    3. asserts that a distinct child model request is made and contains the parent prompt.

    Results on current main:

    • RED, unmodified production path: deterministic collab spawn failed: no thread with id: <root>; no child request
    • GREEN, experimental live-history fallback: new regression test passed twice consecutively
    • Non-regression check: the existing persistent full-history/model-precedence test passed with the experimental fallback present

    This establishes that the persistent-store read for an ephemeral parent is the immediate cause. The experimental fallback is not a production-ready fix.

    Why simply copying live response items is insufficient

    ContextManager contains state beyond its annotated response items, including reference-context and world-state baselines. The persisted reconstruction path also restores turn context, compaction/window lineage, prior effective settings, and related retained state.

    Mapping only live response items to RolloutItem::ResponseItem can therefore lose invariants that the existing full-history tests intentionally protect, including:

    • reference-context and world-state identity after compaction;
    • current-time reminder de-duplication;
    • multi-agent-mode transition/de-duplication;
    • inherited compaction checkpoint/window IDs;
    • effective model/settings precedence;
    • prompt-cache behavior and avoidance of duplicate/conflicting developer context.

    The basic proof test verifies child creation and prompt inheritance only; it does not prove those invariants.

    Recommended implementation direction

    1. Add an atomic, typed live fork snapshot for ephemeral parents, owned by Session/CodexThread (or another context-owning layer), rather than reconstructing an ad hoc list in spawn.rs.
    2. Preserve the annotated model history plus the state needed to reconstruct reference-context baseline, world-state baseline, selected capability roots, compaction/window lineage, and relevant effective turn settings.
    3. Keep the existing ThreadStore reconstruction path unchanged for non-ephemeral parents.
    4. Apply the same sanitizer/truncation logic to both snapshot sources.
    5. If a complete baseline cannot be preserved, explicitly force a context rebuild rather than claiming that reference context was preserved.
    6. Do not solve this by persisting ephemeral sessions; that would violate the --ephemeral contract.
    7. Improve the error boundary so that "live parent exists but persistent fork history is unavailable" is distinguishable from a genuinely unknown thread. Ideally the supported capability should work; otherwise reject it early with a structured error.

    Suggested regression matrix

    • V2 ephemeral: omitted fork_turns, explicit all, positive integer with exact last-N verification, and none control
    • custom role and default role
    • legacy and paginated history modes where supported
    • compacted parent under TokenBudget: no duplicate context and correct child identity/window lineage
    • CurrentTimeReminder and MultiAgentModeInstructions de-duplication
    • selected capability-root/environment inheritance
    • nested ephemeral child spawning another child
    • failed spawn releases agent residency/slots
    • codex exec --json exposes a structured spawn failure and a non-success/degraded outcome rather than a silent successful exit (possibly tracked in MultiAgentV2: Ultra codex exec --ephemeral cannot find root thread, then exhausts agent slots #33672)
    • local mock provider only, to keep the test independent of OpenAI backend/auth
    • all existing non-ephemeral full-history tests remain green

    I have not opened a patch PR because the repository's contribution policy currently asks external contributors to provide issues/RCA/design feedback rather than code PRs. I hope the analysis and test outline are useful for an internal fix.

  4. vd1 commented on Sep 24, 2026

    @vd1

    Confirmed on Codex CLI 0.156.1, macOS 27.0, arm64, using ChatGPT-authenticated CLI and gpt-6-astra / medium. The isolated reproduction below requires no custom named agent, repository, research data or external tools.

    The useful distinction in our probes is whether the helper inherits the parent conversation.

    Controlled comparison

    Both cases used --no-daemon, --ignore-user-config, read-only sandbox, disabled shell tool, one concurrent helper, and the same inherited-context task. Only --ephemeral differed between the invocations; each ran in its own scratch directory with its own harmless marker.

    Parent session Requested helper context Observed result
    Ephemeral fork_turns="all" Spawn failed with collab spawn failed: no thread with id: <parent-thread-id>
    Persistent fork_turns="all" Spawn succeeded; helper returned the marker from the inherited parent conversation

    For the successful persistent control, the saved session trace confirms an actual collaboration.spawn_agent call with fork_turns="all", a successful tool response naming the child task, and a subsequent wait_agent. This is not based solely on the parent's assertion that delegation succeeded.

    The ephemeral failure occurred in about 11 seconds; the persistent control finished in about 17 seconds. Each probe had a 120-second external timeout. The failed spawn still resulted in CLI exit code 0 because the parent reported the tool error normally.

    An earlier pair of fresh-context probes (fork_turns="none") reported the expected arithmetic answer without a router error in both modes. The persistent fresh-context trace also confirms a real spawn. Evidence for the ephemeral fresh-context case is weaker because it has no persisted parent trace. We therefore do not claim that all ephemeral delegation fails; inherited-context delegation is the directly reproduced failure.

    Minimal failing invocation

    Set WORK to a fresh, empty scratch directory, then run:

    codex --no-daemon -a never exec \
      --ephemeral \
      --ignore-user-config \
      --skip-git-repo-check \
      --sandbox read-only \
      -c 'shell_environment_policy.inherit="none"' \
      -c 'features.shell_tool=false' \
      -c 'agents.enabled=true' \
      -c 'agents.max_concurrent_threads_per_session=1' \
      -c 'model_reasoning_effort="medium"' \
      --model gpt-6-astra \
      --color never --json \
      --output-last-message "$WORK/result.txt" \
      -C "$WORK" - <<'PROMPT'
    This is a native subagent context-inheritance test, not a coding task.
    The context marker is CONTEXT-example-123.
    Spawn exactly one helper with the full inherited conversation
    (fork_turns="all" if supported). Do not choose fresh or empty context.
    Give the helper this task without copying the marker value into its message:
    "Read the context marker from the inherited parent conversation and return
    only that marker. Do not use tools, read files, or spawn helpers."
    Wait for the helper using the native agent tools. Do not read files, run shell
    commands, browse, or inspect unrelated threads. Report spawned, helper_result
    and error. If spawning fails, report the actual error and stop; do not retry,
    switch to fresh context, or substitute your own answer.
    PROMPT

    The prompt above uses a replacement marker and shortened wording, preserving the tested task and constraints. The probe subprocess inherited only HOME, PATH, LANG and TMPDIR from its launcher. The persistent control removed --ephemeral and otherwise kept the invocation unchanged.

    Error and workaround

    ERROR codex_core::tools::router: error=collab spawn failed: no thread with id: <parent-thread-id>
    

    The missing ID is the same parent ID emitted by thread.started, not a child ID.

    Removing --ephemeral restores inherited-context delegation in this comparison, while retaining --no-daemon and the permission restrictions. This narrows the trigger, but does not establish the internal implementation cause.

    Ideally inherited-context delegation would also work for an ephemeral parent. Otherwise the unsupported combination should be rejected clearly, rather than exposing a spawn operation that cannot resolve its caller.

    Related: #33672. We did not retry failed spawns, so this reproduction does not test the agent-slot exhaustion described there. Only sanitized reproduction details are included here; no research material, account identifiers, raw session files or authentication data are attached.

  5. added a commit that references this issue on Oct 2, 2026
  6. etraut-openai commented on Oct 12, 2026

    @etraut-openai
    Contributor

    (response from codex)

    Closing as a duplicate of #33672, which tracks exec's ephemeral root-thread lookup failure during subagent spawn. This report adds the persistent-mode control and Windows/macOS reproductions; its later no-custom-agent case shows this isn't just an agent TOML discovery problem. Please continue testing the ephemeral/persistent distinction there.

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 CLIbugSomething isn't workingexecIssues related to the `codex exec` subcommandsessionIssues involving session (thread) management, resuming, forking, naming, archivingsubagentIssues involving subagents or multi-agent featureswindows-osIssues related to Codex on Windows systems

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions