Warn on invalid config enum values#21111
Conversation
Allow config loading to continue when enum-valued settings contain invalid values. Invalid enum entries are removed from the layer before merging and surfaced through startup config warnings, while unrelated valid settings keep loading normally. Co-authored-by: Codex <noreply@openai.com>
Defer invalid enum handling until after config layers are assembled. This keeps layer loading raw, removes invalid enum values from the final effective config, and reports warnings with the dotted field path and invalid value only. Co-authored-by: Codex <noreply@openai.com>
Replace the explicit config enum sanitizer with a generic deserialize retry loop over the assembled TOML. Unknown enum variant errors remove the offending field, append a warning with the field path and invalid value, and retry deserialization. Co-authored-by: Codex <noreply@openai.com>
Store selected config enum fields as Lenient<T> so invalid values remain visible after deserialization and can be reported as startup warnings while valid consumers unwrap at runtime. Remove the older retry-loop sanitizer now that warnings come from the typed config tree. Co-authored-by: Codex <noreply@openai.com>
Cover the full ConfigBuilder load path for invalid enum values so startup warnings are asserted alongside valid config that still applies. Co-authored-by: Codex <noreply@openai.com>
Remove the explicit invalid_enum_warnings tree walk and have Lenient::into_valid append warning messages when invalid values are consumed. Keep higher-level resolvers returning values instead of accepting active profile and warning sink plumbing. Co-authored-by: Codex <noreply@openai.com>
Keep Lenient::into_valid as the silent projection helper and add into_valid_with_warning for runtime config consumption that should emit startup warnings. Co-authored-by: Codex <noreply@openai.com>
Remove the local warning unwrap helpers so runtime config consumption calls Lenient::into_valid_with_warning directly instead of hiding the API behind wrappers. Co-authored-by: Codex <noreply@openai.com>
There was a problem hiding this comment.
💡 Codex Review
codex/codex-rs/exec/src/lib.rs
Line 359 in 9bc30cf
cli_auth_credentials_store is now Option<Lenient<AuthCredentialsStoreMode>>, but this call still uses unwrap_or_default() as if it were Option<AuthCredentialsStoreMode>. Lenient<T> has no Default, so codex-exec no longer compiles when this path is checked. Convert through into_valid()/warning handling before passing it on.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Update the TUI config consumer to unwrap the lenient CLI auth credential store before passing it to the cloud requirements loader. Co-authored-by: Codex <noreply@openai.com>
Fix the exec cloud requirements path after cli_auth_credentials_store became lenient. Co-authored-by: Codex <noreply@openai.com>
Address clippy and compile failures from wrapping config enums in Lenient. Co-authored-by: Codex <noreply@openai.com>
Update test expectations after invalid enum values no longer fail whole config deserialization. Co-authored-by: Codex <noreply@openai.com>
jif-oai
left a comment
There was a problem hiding this comment.
TBH I don't think this is the good layer to integrate this
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b1c09a4426
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e1d8a67d5d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e3de954486
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| #[serde_as(deserialize_as = "DefaultOnError")] | ||
| pub approval_policy: Option<AskForApproval>, |
There was a problem hiding this comment.
Route ConfigToml parsing through the lenient layer merge
Adding DefaultOnError here makes every direct try_into::<ConfigToml>() silently drop invalid enum values. Callers like app-server config read/write still deserialize the raw flattened effective_config(), so a user approval_policy = "bogus" over a managed never is returned/validated as unset instead of falling back per layer or being rejected.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
I think this comment is still right now?
I just tried with approval_policy = { sandbox_approval = true } and this silently become None...
There was a problem hiding this comment.
The idea is to do best effort of catching warnings. I catch wrong enum values but it would be harder to catch all ignored failures
There was a problem hiding this comment.
💡 Codex Review
codex/codex-rs/config/src/loader/mod.rs
Line 908 in 0e3631c
This prefers the serialized typed value whenever the key exists in resolved. For non-Option enum fields with defaults, e.g. [tui] notification_method = "loudly", lenient parsing drops the bad value, serialization reintroduces "auto", and this line replaces the original invalid value before the warning pass. The bad config is silently accepted with the default and no startup warning/fallback.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| let (sanitized_layer, _typed_layer, layer_warnings) = | ||
| deserialize_with_enum_warnings(normalized_layer)?; |
There was a problem hiding this comment.
Avoid validating each layer before effective overrides apply
Deserializing every layer here makes a lower-precedence non-enum error fatal before a higher layer can override it. For example, a system layer with model_context_window = "bad" and a user/session layer with model_context_window = 100000 used to merge to a valid effective config, but now the system layer parse returns an error and aborts startup.
Useful? React with 👍 / 👎.
|
@codex review this |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b487b3592d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f366ac3326
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
codex/codex-rs/core/src/config/mod.rs
Lines 1024 to 1029 in a21345c
The normal config is scanned for enum warnings before this branch, but the lockfile config is then loaded through a fresh ConfigLayerStack with no warning scan. Since ConfigLockfileToml contains ConfigToml, the new DefaultOnError handling can silently drop invalid enum values from the lockfile with no startup warning, so locked sessions may ignore bad locked settings without notice.
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if let Some(warning) = enum_value_warnings(value).into_iter().next() { | ||
| return Err(SerdeError::custom(warning)); |
There was a problem hiding this comment.
Validate only edited enum values on config writes
Because startup now tolerates invalid enum values, a user can have an ignored stale enum in config and still run Codex. This validation scans the whole user/effective config after any edit, so changing an unrelated key is rejected until the stale enum is fixed. The write path comment/intent is to reject newly provided invalid enum values, not block all unrelated writes.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3f55561327
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ) -> Result<(TomlValue, ConfigToml, Vec<String>), toml::de::Error> { | ||
| let merged = self.effective_config(); | ||
| let warnings = invalid_enum_warnings(&merged); | ||
| let typed = merged.clone().try_into::<ConfigToml>()?; |
There was a problem hiding this comment.
Make every warned enum non-fatal
Fresh evidence after the earlier nested-enum review: projects.<id>.trust_level and tool_suggest.*[].type still deserialize through strict enum fields. The warning pass will report these invalid values, but this try_into::<ConfigToml>() still returns an error, so startup/config-lock replay abort instead of continuing with a warning.
Useful? React with 👍 / 👎.
jif-oai
left a comment
There was a problem hiding this comment.
I think the direction is better but there are stills edge cases (that will multiply in the future) that makes it not reliable...
A nice macro would be amazing here but I know people don't like macros
| #[serde_as(deserialize_as = "DefaultOnError")] | ||
| pub approval_policy: Option<AskForApproval>, |
There was a problem hiding this comment.
I think this comment is still right now?
I just tried with approval_policy = { sandbox_approval = true } and this silently become None...
| // This helper returns ConfigToml directly, so enum warnings are intentionally | ||
| // not surfaced here. The full ConfigBuilder path is responsible for | ||
| // attaching them to startup warnings. | ||
| let (_merged_toml, cfg, _enum_warnings) = config_layer_stack |
There was a problem hiding this comment.
This leaves codex exec on a silent path: it calls this helper directly, so invalid enum values get defaulted but the warning never reaches the user. Should we surface the warnings here too?
There was a problem hiding this comment.
it still consumes the startup warnings. try just codex hi with approval_policy = "potato"
| /// Defaults to `true`. | ||
| #[serde(default, rename = "notifications")] | ||
| #[serde_as(deserialize_as = "DefaultOnError")] | ||
| pub notifications: Notifications, |
There was a problem hiding this comment.
Same issue as before. If I do for example notifications = "xxx", this silently default...
There was a problem hiding this comment.
Yeah, because it's an enum
| #[schemars(deny_unknown_fields)] | ||
| pub struct ToolSuggestConfig { | ||
| #[serde(default)] | ||
| #[serde_as(deserialize_as = "VecSkipError<_>")] |
There was a problem hiding this comment.
I'm not sure on this one but this seems to silently drop malformed items in the vec. Which is exactly what we don't want right?
There was a problem hiding this comment.
Yeah, because it has enum
| ), | ||
| ( | ||
| Some(&TomlValue::String("make-it-so".to_string())), | ||
| None, |
There was a problem hiding this comment.
I still don't get why this is not workspace-write? while it is supposed ot be the higher precedence https://github.com/openai/codex/pull/21111/changes#diff-54f9773abe3add13d5213a4465a234a42359e43793dc79d8f4b63bc3ff849418R322
Why
A single invalid enum-valued setting in
config.tomlshould not make the whole assembled config unusable. The rest of the config may still contain valid model, sandbox, profile, tool, hook, or telemetry settings that Codex can safely apply.Review feedback also pushed the design away from making invalid values representable throughout
ConfigToml. The raw invalid state should stay at the config boundary: warn from the TOML that is about to be loaded, then continue handing normal typed config values to runtime consumers.What Changed
codex-rs/config/src/enum_warnings.rs.invalid_enum_warnings, so call sites use the same vocabulary: detect invalid enum leaves and return advisory warnings.properties,additionalProperties,items,$ref,allOf,anyOf, andoneOf.[config]subtree before typed lockfile deserialization.type,model_providers.*.wire_api, and OTEL exporters/protocols.codex-rs/core/config.schema.jsonfor the config shape/default updates.Verification
Added focused coverage for:
oneOfvariants and non-string union branches not warning incorrectly;Ran
just write-config-schemaafter the schema-affecting changes. Local test execution is intentionally left to PR CI per repo guidance for online PRs.