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.
Summary
PR #27711 added
supports_websockets = falsevia anopenai-proxymodel provider in the Codex TOML config, but the generated TOML has a key-ordering bug that causes Codex to silently ignore themodel_provider = "openai-proxy"directive and fall back to the defaultOpenAIprovider — which still uses WebSockets → 401 Unauthorized.Root Cause
In TOML, all key-value pairs following a
[table]header belong to that table. In the generatedconfig.toml,model_provider = "openai-proxy"is emitted after the[shell_environment_policy]table header, making it a sub-key ofshell_environment_policyrather than a root-level Codex directive.Broken output (current):
Correct output (needed):
Code Location
File:
pkg/workflow/codex_mcp.goThe issue is the call order inside
RenderMCPConfig():Fix
Swap the call order so
renderOpenAIProxyProviderTomlis called beforerenderShellEnvironmentPolicyToml:Evidence
Failing run (smoke-codex, branch
copilot/fix-supports-websockets-flag):provider=OpenAI ... transport="responses_websocket" ... failed to connect to websocket: HTTP error: 401 Unauthorized, url: wss://api.openai.com/v1/responsessupports_websockets = falsebut Codex ignores it due to the TOML nestingSuccessful run for comparison (gh-aw-firewall, compiled with correct ordering):
model_provider_id: "openai-proxy" ... supports_websockets: false— provider used correctlyThe 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-proxywith Codex 0.121.0+ (which prefersgpt-5.3-codexover WebSocket) will fail with WebSocket 401 despite PR #27711 being merged.