Skip to content

[Bug] Per-role model_fallback causes Codex 0.146.0 to reject custom agent TOML #1190

Description

@eingo19

Client or integration

Codex CLI and Codex App

Area

Proxy and routing / Catalog and models / Documentation

Summary

OpenCodex documents the per-role model_fallback field in $CODEX_HOME/agents/*.toml as the second source in its spawned-worker fallback priority:

  1. requested primary model
  2. the role's model_fallback
  3. global subagentModelFallback

However, Codex CLI 0.146.0 strictly deserializes custom-agent TOML files. If a role contains model_fallback, Codex reports it as an unknown field and skips the entire role definition. The role therefore never enters the multi_agent_v1 runtime registry, so OpenCodex cannot apply the documented fallback chain.

This appears to be a compatibility defect between OpenCodex's documented per-role fallback extension and Codex's native custom-agent schema, rather than a provider-specific model failure.

Related feature: #374
Current documentation: https://opencodex.me/guides/sub-agent-surface/#fallback-chains

Reproduction

The failure was reproduced in an isolated temporary CODEX_HOME; the working global configuration was not modified.

  1. Create $CODEX_HOME/config.toml:
[features]
multi_agent = true
  1. Create $CODEX_HOME/agents/test_worker.toml:
name = "test_worker"
description = "Minimal fallback compatibility test"
model = "opencode-go/deepseek-v4-flash"
model_fallback = ["deepseek/deepseek-v4-flash", "gpt-5.6-luna"]

developer_instructions = """
Return a short result.
"""
  1. Run:
$env:CODEX_HOME = "C:\path\to\isolated-codex-home"
codex doctor --all --no-color --ascii
  1. Observe that Codex loads config.toml successfully but skips test_worker.toml.

  2. Remove only the model_fallback line and restart Codex.

  3. The role is then registered and appears in the current task's multi_agent_v1 Available roles.

Real configuration impact

The original setup contained five custom roles:

  • luna_worker.toml: no model_fallback; registered successfully
  • deepseekflash_go_worker.toml: had model_fallback; not registered
  • deepseekflash_worker.toml: had model_fallback; not registered
  • glm52_worker.toml: had model_fallback; not registered
  • kimik3_worker.toml: had model_fallback; not registered

After removing only model_fallback from the four affected files and fully restarting Codex, all five custom roles were present in the live runtime registry:

deepseekflash_go_worker
deepseekflash_worker
glm52_worker
kimik3_worker
luna_worker

Version

  • OpenCodex: 2.10.0
  • Codex CLI: 0.146.0
  • Codex installation: npm package
  • Multi-agent mode: v1
  • agents.enabled: true

Operating system

Windows 11, build 26200, x86_64

Provider and model

The deserialization failure happens before model routing, so it is not provider-specific.

The affected real-world roles used:

  • opencode-go/deepseek-v4-flash
  • deepseek/deepseek-v4-flash
  • opencode-go/glm-5.2
  • opencode-go/kimi-k3
  • fallback target gpt-5.6-luna

Logs or error output

Configuration
  [!!] config       config loaded
      config.toml parse        ok
      startup warnings         1
      startup warning          Ignoring malformed agent role definition: failed to deserialize agent role file at C:\path\to\isolated-codex-home\agents\test_worker.toml: unknown field `model_fallback`

The isolated codex doctor run exited non-zero because the temporary home intentionally contained no authentication credentials. That is unrelated to the agent parsing warning; config.toml parse was ok, and the exact startup warning above was emitted before any model request.

Expected behavior

A per-role fallback mechanism documented by OpenCodex should not invalidate the underlying Codex custom-agent definition.

At minimum, OpenCodex should detect that the installed Codex runtime rejects model_fallback and provide a clear compatibility warning or migrate the fallback metadata elsewhere.

Actual behavior

Codex rejects the complete custom-agent TOML before the role can be registered. From the user's perspective:

  1. the TOML file exists;
  2. the configured primary and fallback models both exist;
  3. the role is nevertheless absent from multi_agent_v1 Available roles;
  4. spawning with that agent_type is rejected as an unregistered role.

Removing the documented fallback field fixes registration, but loses the per-role proxy fallback metadata.

Root-cause analysis

Codex's agent-role TOML schema denies unknown fields. model_fallback is an OpenCodex extension, but it is stored in the same TOML document that Codex must deserialize before exposing the role.

The proxy may be able to read this field independently, but Codex rejects the role first. As a result, the documented fallback feature prevents the role from reaching the spawn surface it is meant to protect.

Suggested fixes

Prefer keeping OpenCodex-only metadata outside files parsed by native Codex. For example:

  1. Store per-role fallback chains in ~/.opencodex/config.json, keyed by role name or agent TOML path.
  2. Use a separate OpenCodex sidecar metadata file.
  3. Add a version/schema compatibility check and refuse to configure unsupported per-role TOML fields.
  4. Provide an ocx doctor diagnostic that scans $CODEX_HOME/agents/*.toml and warns that model_fallback will make affected Codex versions skip the role.
  5. If future Codex versions add a native fallback field, gate direct TOML emission on confirmed schema support.

The global subagentModelFallback mechanism can remain in OpenCodex config because it does not contaminate Codex-owned role TOML.

Workaround

Remove model_fallback from every $CODEX_HOME/agents/*.toml file, restart Codex completely, and define fallback behavior through:

  • OpenCodex's global fallback configuration; or
  • main-agent routing instructions that explicitly retry a fallback role/model.

Checks

  • I searched existing issues and documentation.
  • I removed secrets, tokens, account details, request credentials, and personal data.
  • The failure was reproduced in an isolated temporary CODEX_HOME.
  • Registration was verified again after removing only the unsupported field.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions