spawn_agent(model="gpt-5.4-mini") produces child thread metadata with model="gpt-5.4"

Resolved 💬 5 comments Opened Mar 19, 2026 by himwichboys Closed Jun 22, 2026
💡 Likely answer: A maintainer (github-actions[bot], contributor) responded on this thread — see the highlighted reply below.

What version of the Codex App are you using (From “About Codex” dialog)?

26.317.21539 (1088)

What subscription do you have?

Pro

What platform is your computer?

Darwin 25.3.0 arm64 arm

What issue are you seeing?

## Summary

Codex subagents spawned with `model: "gpt-5.4-mini"` are later recorded in the child thread metadata as `model: "gpt-5.4"`.

This makes the child thread UI show `GPT-5.4`, even though the parent explicitly requested `gpt-5.4-mini`.

## Environment

- Product: Codex desktop app
- Version: `0.116.0-alpha.1`
- Date observed: `2026-03-19`
- Timezone: `America/New_York`

## Expected behavior

When the parent thread calls `spawn_agent` with:

```json
{
  "model": "gpt-5.4-mini",
  "reasoning_effort": "xhigh"
}

the spawned child thread should preserve and display that same model in its persisted metadata and UI.

Actual behavior

The parent thread log shows the subagent was spawned with model: "gpt-5.4-mini", but the child thread’s persisted turn_context records model: "gpt-5.4".

What steps can reproduce the bug?

Minimal repro

  1. In Codex desktop, ask the parent agent to spawn a subagent with:
  • model: "gpt-5.4-mini"
  • reasoning_effort: "xhigh"
  1. Open the spawned child thread
  2. Check the child thread’s displayed model and persisted session metadata
  3. Observe that the child thread is recorded as gpt-5.4

Evidence

Parent thread spawn_agent call:

{
  "agent_type": "explorer",
  "fork_context": true,
  "model": "gpt-5.4-mini",
  "reasoning_effort": "xhigh",
  "message": "Perform a bug-focused review..."
}

Child thread persisted context:

{
  "type": "turn_context",
  "payload": {
    "model": "gpt-5.4",
    "collaboration_mode": {
      "mode": "plan",
      "settings": {
        "model": "gpt-5.4",
        "reasoning_effort": "xhigh"
      }
    }
  }
}

Notes

This proves that:

  • the parent requested gpt-5.4-mini
  • the child thread metadata was recorded as gpt-5.4

It does not prove from logs alone whether inference actually ran on mini or was remapped before execution.

So the issue appears to be in one of:

  • spawn_agent model handling
  • child-thread metadata persistence
  • UI provenance/display of spawned model

Impact

This makes it hard to trust or verify delegated model selection for:

  • cost-sensitive fan-out reviews
  • performance comparisons
  • debugging subagent behavior differences

Suggested investigation

Please check whether:

  • spawn_agent.model is being dropped or normalized during child-thread creation
  • child turn_context.model is defaulting to the parent model
  • the UI is reading thread-default model instead of actual spawned-run model provenance

```md
### Sanitized log excerpts

Parent session:
```json
{"type":"response_item","payload":{"type":"function_call","name":"spawn_agent","arguments":"{\"agent_type\":\"explorer\",\"fork_context\":true,\"model\":\"gpt-5.4-mini\",\"reasoning_effort\":\"xhigh\",\"message\":\"Perform a bug-focused review...\"}"}}

Child session:

{"type":"turn_context","payload":{"model":"gpt-5.4","collaboration_mode":{"mode":"plan","settings":{"model":"gpt-5.4","reasoning_effort":"xhigh"}}}}

### What is the expected behavior?

_No response_

### Additional information

When a parent thread spawns a subagent with an explicit model override like `model: "gpt-5.4-mini"` and `reasoning_effort: "xhigh"`, the spawned child thread should preserve that exact model in its persisted metadata and display it consistently in the UI.

Concretely, I expect:
- the child thread metadata to record `gpt-5.4-mini`
- the child thread UI/model picker to show `GPT-5.4-mini`
- no silent normalization or fallback to `gpt-5.4` unless Codex explicitly reports that fallback to the user

View original on GitHub ↗

5 Comments

github-actions[bot] contributor · 5 months ago

Potential duplicates detected. Please review them and close your issue if it is a duplicate.

  • #15170
  • #14671
  • #13849

Powered by Codex Action

jhste102lab · 5 months ago

I can still reproduce this on current main (be5afc65d31728c11d67c589cebbc64a87979e74) in the CLI, not just in Desktop/VS Code.

What I verified on March 27, 2026:

  • Built Codex locally from source at be5afc6
  • Ran a minimal codex exec repro that spawns exactly one explorer
  • Parent spawn_agent call explicitly requested:
  • agent_type: "explorer"
  • model: "gpt-5.4-mini"
  • reasoning_effort: "xhigh"
  • Spawned child session was created successfully and completed its task

But the persisted child session metadata still records the inherited/default model instead of the requested explorer override:

  • Parent session: 019d2d8c-eef0-7f03-baa3-6aa4605e3afe
  • Child session: 019d2d8d-2697-76d0-99fa-ac765ee8db15

Observed in the logs:

Parent session:
```json
{"type":"function_call","name":"spawn_agent","arguments":"{\"agent_type\":\"explorer\",\"fork_context\":false,\"model\":\"gpt-5.4-mini\",\"reasoning_effort\":
\"xhigh\",...}"}

Child session turn_context:

{
"model": "gpt-5.4",
"collaboration_mode": {
"mode": "default",
"settings": {
"model": "gpt-5.4",
"reasoning_effort": "medium"
}
},
"effort": "medium"
}

So at minimum, this is still a child-session metadata / provenance bug on current main.

I still cannot prove from logs alone whether actual inference ran on gpt-5.4-mini and only the metadata is wrong, or whether the override is being lost before
execution. But the persisted child metadata is definitely inconsistent with the parent spawn_agent request.

This seems broader than the original desktop-only framing, since I reproduced it in codex exec on a source build from current main.

If useful, I can provide a more compact minimal repro prompt/command shape as well.

ozan-s · 4 months ago

I'm also seeing sub-agents ignore the model I request. When I spawn with model: "gpt-5.4-mini", the agent runs on the parent's model ("gpt-5.4") instead.

This only happens with the "explorer" agent type. The "explorer" role has an associated config file (explorer.toml) that triggers a full config rebuild during spawn. That rebuild preserves model_provider but not model, so the requested model is lost and falls back to the parent session's model. The "default" role has no config file, so it skips the rebuild entirely and respects the model you specify.

Workaround: Use "default" as the agent type instead of "explorer". The model selection will then work as expected.

Here is the proof:

› Launch three agents, one for each type with a dummy request (read only). Launch them all with gpt-5.4-mini high reasoning.

• Using the requested setup now: I’ll spawn three subagents in parallel (default, explorer, worker), each with a read-only dummy task, all on gpt-5.4-mini with high reasoning.

• Spawned Bohr [default] (gpt-5.4-mini high)
  └ Dummy read-only task: inspect repository root and report the first 10 top-level entries. Do not modify any files or run destructive commands.

• Spawned Tesla [explorer] (gpt-5.3-codex medium)
  └ Dummy read-only task: find AGENTS.md files in this workspace and return their paths only. Read-only; do not modify files.

• Spawned Pasteur [worker] (gpt-5.4-mini high)
  └ Dummy read-only task: run a safe read-only check (`git status --short`) and report output summary. Do not edit files, stage changes, or run destructive comma...
ozan-s · 4 months ago

And here is the proposed fix:

⏺ Root Cause Identified

  The bug is an ordering issue in the spawn handler. Both V1 and V2 have the same problem.

  The exact flow (both multi_agents/spawn.rs and multi_agents_v2/spawn.rs):

  1. build_agent_spawn_config — creates config with config.model = parent_model (e.g., "gpt-5.4") — multi_agents_common.rs:226
  2. apply_requested_spawn_agent_model_overrides — sets config.model = "gpt-5.4-mini" (what you requested) — multi_agents_common.rs:298
  3. apply_role_to_config — rebuilds the entire Config from scratch using the config layer stack — role.rs:69

  Why the model gets lost:

  apply_role_to_config calls build_next_config which rebuilds the config from layers. Inside reload::reload_overrides (role.rs:253-262), the function preserves cwd and model_provider, but does NOT preserve model:

  fn reload_overrides(config: &Config, preserve_current_provider: bool) -> ConfigOverrides {
      ConfigOverrides {
          cwd: Some(config.cwd.to_path_buf()),
          model_provider: preserve_current_provider.then(|| config.model_provider_id.clone()),
          // model is NOT preserved! Falls back to Default::default() → None
          ..Default::default()
      }
  }

  Then in Config::load_config_with_layer_stack (mod.rs:2440):
  let model = model.or(config_profile.model).or(cfg.model);
  //          ^^^^ None (from overrides) → falls through to profile/global config model → "gpt-5.4"

  So the requested "gpt-5.4-mini" set in step 2 is wiped out by the config rebuild in step 3, and the agent inherits the parent's model from the config layers.

  Why it only affects certain agent types:

  The "default" role has config_file: None, so apply_role_to_config_inner returns early without rebuilding. But "explorer" has config_file: Some("explorer.toml") — even though the file is empty, the rebuild still
  runs and wipes the model override.

  Fix

  The fix is to swap the order: apply the model override AFTER the role config rebuild.

  In both multi_agents/spawn.rs and multi_agents_v2/spawn.rs, change from:

  apply_requested_spawn_agent_model_overrides(...)?;  // Sets requested model
  apply_role_to_config(&mut config, role_name)?;       // Rebuilds config, LOSES the model

  To:

  apply_role_to_config(&mut config, role_name)?;       // Rebuilds config first
  apply_requested_spawn_agent_model_overrides(...)?;    // THEN set requested model
Keesan12 · 3 months ago

Even if inference is actually running on mini, the metadata mismatch is still a real bug because cost and behavior analysis depend on the persisted receipt, not just the hidden execution path. I would treat this as provenance, not cosmetics: requested model, resolved model, displayed model, and actual execution model should either match or be shown explicitly when they do not. We ended up needing that exact split in MartinLoop once fan-out comparisons got more cost-sensitive.