config.toml migration generates a permissions.<name> block that fails --strict-config parsing; sandbox_workspace_write.network_access silently ignored in static config
What version of Codex CLI is running?
codex-cli 0.149.1
What platform is your computer?
Linux (Ubuntu-based), x86_64
What issue are you seeing?
After npm install -g @openai/codex@latest auto-migrated my ~/.codex/config.toml, it wrote a default_permissions = "protect-env" block using the newer [permissions.<name>] profile system:
default_permissions = "protect-env"
[permissions.protect-env]
extends = ":workspace"
[permissions.protect-env.filesystem]
glob_scan_max_depth = 8
[permissions.protect-env.filesystem.":workspace_roots"]
"**/*.env" = "deny"
Running with --strict-config against this exact, unmodified, migration-generated config fails to parse:
Error loading config.toml:
/home/amlan/.codex/config.toml:8:2: data did not match any variant of untagged enum FilesystemPermissionToml
|
8 | [permissions.protect-env]
| ^^^^^^^^^^^
Without --strict-config, this same block is silently ignored — no error, but default_permissions = "protect-env" appears to be a no-op, since sandboxed sessions always fall back to the stock :workspace sandbox defaults (.git read-only, network access disabled) regardless of the profile's contents.
Separately (possibly related, possibly a separate default-change from an update): setting the older/documented
[sandbox_workspace_write]
network_access = true
writable_roots = ["/some/path"]
at the config.toml root (or via a -p profile file) is also silently ignored — no error, no effect on the sandbox's writable roots or network access shown in the session banner. The equivalent -c sandbox_workspace_write.network_access=true CLI override does work at runtime, so there's a discrepancy between how this key is honored via static config-file loading vs. a -c override.
What steps can reproduce the bug?
- Have Codex CLI auto-update to 0.149.1 (or otherwise obtain a
config.tomlwith a migration-generated[permissions.<name>]block, e.g. via theprotect-envdeny-.envmigration). - Run any command with
--strict-config, e.g.:
````
codex exec --strict-config -c 'default_permissions="protect-env"' "echo hi"
- Observe the parse error on the exact block the migration itself generated:
````
data did not match any variant of untagged enum FilesystemPermissionToml
- Separately, confirm
sandbox_workspace_write.network_access = trueset inconfig.toml(root level or via-p <profile>.config.toml) has no effect —git fetch/curlinside the sandbox still fail withCould not resolve host, and the session banner doesn't show(network access enabled). The same setting via-c sandbox_workspace_write.network_access=trueon the command line does work.
Impact
Since the mismatch is silently swallowed outside --strict-config, users have no visibility that their permissions profile isn't applying — sandboxed sessions just behave as if no custom profile exists, with no error or warning. Given .git is read-only and network is disabled by default under :workspace, this makes basic git fetch/git push/web-search workflows fail with no indication that the intended permissions override never took effect.
6 Comments
Potential duplicates detected. Please review them and close your issue if it is a duplicate.
Powered by Codex Action
Not a duplicate of #39996 — that report is about the remote ChatGPT desktop app (macOS→SSH, app-server) ignoring
writable_rootsfor newly created remote tasks. This issue is about the localcodexCLI: the auto-migrated[permissions.<name>]block inconfig.tomlfails to parse againstFilesystemPermissionTomlunder--strict-config(silently swallowed otherwise), andsandbox_workspace_write.network_accessset statically in config has no effect while the equivalent-coverride works. Different component (CLI config parsing vs. remote app-server mount handling), different repro. Keeping open.Correction after re-testing against the actual source and a clean local repro:
Part 1 (
--strict-configschema-mismatch parse error) does NOT reproduce. I re-ran the exact repro command from the issue body against the unmodified config multiple times (codex-cli 0.149.1, clean exit, no error). I also diffedcodex-rs/config/src/permissions_toml.rsbetween therust-v0.149.1tag and currentmain— identical — and confirmed theFilesystemPermissionTomlschema (Access(FileSystemAccessMode) | Scoped(BTreeMap<String, FileSystemAccessMode>)) does accept the config shape from the issue. I believe this part of the original report was a testing mistake on my end (likely cross-contamination from editing the config file around the same time) — retracting it. Apologies for the noise.Part 2 (static
sandbox_workspace_write.network_accesssilently ignored) is real, but the root cause is different from what I described. It's not a parsing/config-loading bug — it's a precedence gap: incodex-rs/core/src/config/mod.rs(around the block handlingprofiles_are_active), legacy[sandbox_workspace_write]settings are only merged in whenusing_implicit_builtin_profileis true, i.e. whendefault_permissionsis unset. As soon as you name any custom profile viadefault_permissions— even one withextends = ":workspace"— the legacy block is dropped entirely with no warning or error. So this is really: legacy[sandbox_workspace_write]config has no effect once a named[permissions.<name>]profile is selected, and there's no diagnostic telling the user their setting was ignored. Narrowing the issue to that.Root cause for the second part of this report (network_access silently ignored)
Found it in
codex-rs/core/src/config/mod.rs, in theprofiles_are_activebranch ofConfigconstruction: legacy[sandbox_workspace_write]settings are only merged in whenusing_implicit_builtin_profileis true, i.e. whendefault_permissionsis unset entirely. The momentdefault_permissionsnames any profile — including a custom one thatextends = ":workspace"(which is exactly what the config-migration writes) —builtin_workspace_write_settingsis set toNoneand the legacy block is dropped, silently, with no diagnostic.This matters because the repo's own docs (
codex-rs/skills/src/assets/samples/imagegen/references/codex-network.md) and the TypeScript SDK README both document[sandbox_workspace_write] network_access = trueas the way to enable network access, with no mention that it stops working once a named permissions profile exists. So a user who follows the documented example, on a config the tool itself auto-migrated to use a named profile, gets no network access and no explanation —git fetch/curl/etc. just fail as if DNS is broken.Proper fix for users hitting this today: move the setting into the new syntax instead:
(
network.modedoes not do this — I initially got this wrong; the field that actually flipsNetworkSandboxPolicyincompile_network_sandbox_policyisnetwork.enabled.)Suggested fix
Per
docs/contributing.md, posting this as analysis/suggested fix rather than a PR. Adds astartup_warningsentry (same mechanism already used for e.g. invalid theme names) when legacy[sandbox_workspace_write]settings are present but ignored due to an active named profile. No behavior change — diagnostics only.Scoped to avoid two failure modes I found in review:
default_permissionsis unset, or when it's explicitly set to the built-in:workspace(existing code already treats explicit:workspaceselection as an intentional legacy opt-out — see the comment at theactive_permission_profilecomputation a few lines below).network_accessis the field actually set, and is phrased as a fact ("this key has no effect under this profile") rather than a claim that fetch/push will fail — since the active profile may already grant network access another way (e.g.:danger-full-access), in which case nothing is actually broken.Also adds one clarifying line + example to
codex-network.mdpointing at the correct new-syntax key, since that's the doc that currently sends users down the broken path.Verified against the current
mainbranch (e3609f2):cargo test -p codex-core --lib config::(494 tests) passes,cargo clippy -p codex-core --lib --testsclean,cargo fmt -p codex-core -- --checkclean. Diff below (not run against the full workspace test suite, so please treat as a starting point rather than a merge-ready patch):If a named
default_permissionsprofile is active, the legacy[sandbox_workspace_write] network_access = truesetting may be ignored silently.Use the setting inside the active permission profile instead:
Then restart Codex and retry the same network operation. Do not substitute
network.modefornetwork.enabled; they are different fields.Limitation: this applies only when a named permission profile is selected. If no named profile is active, the legacy workspace-write setting may still be the applicable path. This is a configuration workaround, not the upstream diagnostic fix.
Regarding
config.tomlpermission block parsing errors and static config enforcement:Managing fine-grained permissions inside complex TOML/JSON configs often introduces syntax drift and parser edge cases across CLI updates.
If you need deterministic, system-level security boundaries that don't depend on application config file parsing:
Vetto enforces immutable OS rules outside the agent runtime:
Key Differences:
config.tomlparsing.