Skip to content

bug: Codex WebSocket 401 — model_provider incorrectly nested inside [shell_environment_policy] in generated TOML config #27957

@lpcox

Description

@lpcox

Summary

PR #27711 added supports_websockets = false via an openai-proxy model provider in the Codex TOML config, but the generated TOML has a key-ordering bug that causes Codex to silently ignore the model_provider = "openai-proxy" directive and fall back to the default OpenAI provider — which still uses WebSockets → 401 Unauthorized.

Root Cause

In TOML, all key-value pairs following a [table] header belong to that table. In the generated config.toml, model_provider = "openai-proxy" is emitted after the [shell_environment_policy] table header, making it a sub-key of shell_environment_policy rather than a root-level Codex directive.

Broken output (current):

[shell_environment_policy]         # ← table opened first
inherit = "core"
include_only = [...]

model_provider = "openai-proxy"   # ← INSIDE [shell_environment_policy]! Bug.

[model_providers.openai-proxy]
name = "OpenAI AWF proxy"
base_url = "http://172.30.0.30:10000"
env_key = "OPENAI_API_KEY"
supports_websockets = false

Correct output (needed):

model_provider = "openai-proxy"   # ← root level ✓

[model_providers.openai-proxy]
name = "OpenAI AWF proxy"
base_url = "http://172.30.0.30:10000"
env_key = "OPENAI_API_KEY"
supports_websockets = false

[shell_environment_policy]
inherit = "core"
include_only = [...]

Code Location

File: pkg/workflow/codex_mcp.go

The issue is the call order inside RenderMCPConfig():

yaml.WriteString("cat > \"/tmp/gh-aw/mcp-config/config.toml\" << " + shellPolicyDelimiter + "\n")
e.renderShellEnvironmentPolicyToml(yaml, ...)    // emits [shell_environment_policy] FIRST
if isFirewallEnabled(workflowData) {
    e.renderOpenAIProxyProviderToml(yaml, ...)   // emits model_provider AFTER → inside the table!
}
yaml.WriteString(shellPolicyDelimiter + "\n")

Fix

Swap the call order so renderOpenAIProxyProviderToml is called before renderShellEnvironmentPolicyToml:

yaml.WriteString("cat > \"/tmp/gh-aw/mcp-config/config.toml\" << " + shellPolicyDelimiter + "\n")
if isFirewallEnabled(workflowData) {
    e.renderOpenAIProxyProviderToml(yaml, ...)   // root-level keys first ✓
}
e.renderShellEnvironmentPolicyToml(yaml, ...)    // table header after ✓
yaml.WriteString(shellPolicyDelimiter + "\n")

Evidence

Failing run (smoke-codex, branch copilot/fix-supports-websockets-flag):

Successful run for comparison (gh-aw-firewall, compiled with correct ordering):

The working run's lock file places model_provider = "openai-proxy" before [shell_environment_policy], so Codex correctly picks it up as a root-level config key.

Impact

All Codex smoke/CI runs using --enable-api-proxy with Codex 0.121.0+ (which prefers gpt-5.3-codex over WebSocket) will fail with WebSocket 401 despite PR #27711 being merged.

Metadata

Metadata

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions