pawn_agent model and reasoning_effort overrides silently dropped when a non-empty agent role is applied
What version of Codex CLI is running?
0.141.0
What subscription do you have?
ChatGPT Plus
Which model were you using?
gpt-5.6-sol
What platform is your computer?
Darwin 26.5.0 arm64 arm
What terminal emulator and version are you using (if applicable)?
Terminal.app (zsh)
Codex doctor report
What issue are you seeing?
When spawn_agent specifies a model or reasoning_effort override, those values are silently discarded if the spawned agent has a non-empty role layer (e.g. one containing developer_instructions). The child agent falls back to
the parent's top-level config.toml model instead of the requested one. No error or warning is emitted — the fallback is completely silent.
In core/src/tools/handlers/multi_agents_v2/spawn.rs:73-84, model overrides are applied before role application:
apply_requested_spawn_agent_model_overrides( // sets config.model = requested model
&session, turn.as_ref(), &mut config,
args.model.as_deref(), args.reasoning_effort.clone(),
).await?;
apply_role_to_config(&mut config, role_name) // rebuilds config, loses model
.await
.map_err(FunctionCallError::RespondToModel)?;
apply_role_to_config (core/src/agent/role.rs:56-83) rebuilds the config via reload::build_next_config, which calls reload_overrides (role.rs:201-214):
fn reload_overrides(
config: &Config,
preserve_current_provider: bool,
preserve_current_service_tier: bool,
) -> ConfigOverrides {
ConfigOverrides {
cwd: Some(config.cwd.to_path_buf()),
model_provider: preserve_current_provider.then(|| config.model_provider_id.clone()),
service_tier: preserve_current_service_tier.then(|| config.service_tier.clone()),
codex_linux_sandbox_exe: config.codex_linux_sandbox_exe.clone(),
main_execve_wrapper_exe: config.main_execve_wrapper_exe.clone(),
..Default::default() // model: None, no model_reasoning_effort field at all
}
}
model_provider and service_tier are explicitly preserved, but model is left as None and model_reasoning_effort does not exist as a field on ConfigOverrides (config/mod.rs:2464-2492).
During the rebuild, config/mod.rs:3685 resolves the model as:
let model = model.or(cfg.model);
Since the override model is None, this falls back to cfg.model — the merged config layers, which carry the parent's config.toml model. The requested model is lost.
Empty roles are unaffected because role.rs:66-71 short-circuits:
if role_layer_toml.as_table().is_some_and(toml::map::Map::is_empty) {
return Ok(());
}
The built-in explorer.toml is empty, so it returns early without rebuilding. Any role with developer_instructions (or any other config key) is non-empty and triggers the full reload, losing the model. This is why the existing
test apply_empty_explorer_role_preserves_current_model_and_reasoning_effort passes — it only covers the empty-role path.
What steps can reproduce the bug?
- Set model = "gpt-5.6-sol" in config.toml.
- Define a custom agent role with developer_instructions but no model field:
name = "my-worker"
description = "A worker agent."
developer_instructions = "You are a worker."
- Call spawn_agent with model = "gpt-5.6-luna" and reasoning_effort = "max".
- Inspect the child session's task_started / first turn_context event.
Parent session JSONL records the requested model:
{"type":"function_call","name":"spawn_agent","arguments":"{\"model\":\"gpt-5.6-luna\",\"reasoning_effort\":\"max\",...}"}
Child session's first turn_context shows the actually-used model:
{"model":"gpt-5.6-sol","effort":"high"}
What is the expected behavior?
The model and reasoning_effort parameters passed to spawn_agent should take precedence over the parent's config.toml model when the role layer does not explicitly set a model or model_reasoning_effort.
Additional information
- reasoning_effort is doubly affected: ConfigOverrides has no model_reasoning_effort field at all, so it cannot be preserved even if the logic tried.
- The only current workaround is to pin model in the role toml itself, but this triggers the "This role's model is set to ... and cannot be changed" lock, making the role's model immutable for all future spawns.
- The expose_spawn_agent_model_overrides config flag only controls whether the model parameter is shown in the tool description to the model — it does not affect runtime precedence.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗