fix(config): retain last-known-good config when config.yaml fails to parse - #60591
Merged
Merged
Conversation
…parse Port from openai/codex#31188: a parse failure in a policy-bearing config file must not silently replace the effective policy with an empty/default one. Codex's load_exec_policy_with_warning replaced the whole exec policy with Policy::empty() when a .rules file failed to parse, silently dropping managed prompt/forbidden rules; the fix preserves the managed policy while still warning. Hermes had the same bug shape in load_config(): a YAML parse error made _load_config_impl() fall through to DEFAULT_CONFIG, dropping every user override — including approvals.deny rules, which are documented to block commands even under --yolo. In a long-running gateway, a user mid-editing config.yaml into broken YAML silently disarmed their own deny rules on the next load. Now, when the process has a last successfully loaded config for that path (_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached under the corrupt file's signature so the broken file isn't re-parsed) and the warning says edits are being ignored until the YAML is fixed. Fresh processes with no last-known-good keep the existing DEFAULT_CONFIG fallback and warning. E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process corruption; fixed file reloads normally; fresh-process fallback unchanged.
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…parse (NousResearch#60591) Port from openai/codex#31188: a parse failure in a policy-bearing config file must not silently replace the effective policy with an empty/default one. Codex's load_exec_policy_with_warning replaced the whole exec policy with Policy::empty() when a .rules file failed to parse, silently dropping managed prompt/forbidden rules; the fix preserves the managed policy while still warning. Hermes had the same bug shape in load_config(): a YAML parse error made _load_config_impl() fall through to DEFAULT_CONFIG, dropping every user override — including approvals.deny rules, which are documented to block commands even under --yolo. In a long-running gateway, a user mid-editing config.yaml into broken YAML silently disarmed their own deny rules on the next load. Now, when the process has a last successfully loaded config for that path (_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached under the corrupt file's signature so the broken file isn't re-parsed) and the warning says edits are being ignored until the YAML is fixed. Fresh processes with no last-known-good keep the existing DEFAULT_CONFIG fallback and warning. E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process corruption; fixed file reloads normally; fresh-process fallback unchanged.
justemu
pushed a commit
to justemu/hermes-agent
that referenced
this pull request
Jul 18, 2026
…parse (NousResearch#60591) Port from openai/codex#31188: a parse failure in a policy-bearing config file must not silently replace the effective policy with an empty/default one. Codex's load_exec_policy_with_warning replaced the whole exec policy with Policy::empty() when a .rules file failed to parse, silently dropping managed prompt/forbidden rules; the fix preserves the managed policy while still warning. Hermes had the same bug shape in load_config(): a YAML parse error made _load_config_impl() fall through to DEFAULT_CONFIG, dropping every user override — including approvals.deny rules, which are documented to block commands even under --yolo. In a long-running gateway, a user mid-editing config.yaml into broken YAML silently disarmed their own deny rules on the next load. Now, when the process has a last successfully loaded config for that path (_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached under the corrupt file's signature so the broken file isn't re-parsed) and the warning says edits are being ignored until the YAML is fixed. Fresh processes with no last-known-good keep the existing DEFAULT_CONFIG fallback and warning. E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process corruption; fixed file reloads normally; fresh-process fallback unchanged.
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…parse (NousResearch#60591) Port from openai/codex#31188: a parse failure in a policy-bearing config file must not silently replace the effective policy with an empty/default one. Codex's load_exec_policy_with_warning replaced the whole exec policy with Policy::empty() when a .rules file failed to parse, silently dropping managed prompt/forbidden rules; the fix preserves the managed policy while still warning. Hermes had the same bug shape in load_config(): a YAML parse error made _load_config_impl() fall through to DEFAULT_CONFIG, dropping every user override — including approvals.deny rules, which are documented to block commands even under --yolo. In a long-running gateway, a user mid-editing config.yaml into broken YAML silently disarmed their own deny rules on the next load. Now, when the process has a last successfully loaded config for that path (_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached under the corrupt file's signature so the broken file isn't re-parsed) and the warning says edits are being ignored until the YAML is fixed. Fresh processes with no last-known-good keep the existing DEFAULT_CONFIG fallback and warning. E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process corruption; fixed file reloads normally; fresh-process fallback unchanged.
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…parse (NousResearch#60591) Port from openai/codex#31188: a parse failure in a policy-bearing config file must not silently replace the effective policy with an empty/default one. Codex's load_exec_policy_with_warning replaced the whole exec policy with Policy::empty() when a .rules file failed to parse, silently dropping managed prompt/forbidden rules; the fix preserves the managed policy while still warning. Hermes had the same bug shape in load_config(): a YAML parse error made _load_config_impl() fall through to DEFAULT_CONFIG, dropping every user override — including approvals.deny rules, which are documented to block commands even under --yolo. In a long-running gateway, a user mid-editing config.yaml into broken YAML silently disarmed their own deny rules on the next load. Now, when the process has a last successfully loaded config for that path (_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached under the corrupt file's signature so the broken file isn't re-parsed) and the warning says edits are being ignored until the YAML is fixed. Fresh processes with no last-known-good keep the existing DEFAULT_CONFIG fallback and warning. E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process corruption; fixed file reloads normally; fresh-process fallback unchanged.
randlee
pushed a commit
to randlee/hermes-agent
that referenced
this pull request
Aug 11, 2026
…parse (NousResearch#60591) Port from openai/codex#31188: a parse failure in a policy-bearing config file must not silently replace the effective policy with an empty/default one. Codex's load_exec_policy_with_warning replaced the whole exec policy with Policy::empty() when a .rules file failed to parse, silently dropping managed prompt/forbidden rules; the fix preserves the managed policy while still warning. Hermes had the same bug shape in load_config(): a YAML parse error made _load_config_impl() fall through to DEFAULT_CONFIG, dropping every user override — including approvals.deny rules, which are documented to block commands even under --yolo. In a long-running gateway, a user mid-editing config.yaml into broken YAML silently disarmed their own deny rules on the next load. Now, when the process has a last successfully loaded config for that path (_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached under the corrupt file's signature so the broken file isn't re-parsed) and the warning says edits are being ignored until the YAML is fixed. Fresh processes with no last-known-good keep the existing DEFAULT_CONFIG fallback and warning. E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process corruption; fixed file reloads normally; fresh-process fallback unchanged.
melon-xf
added a commit
to melon-xf/hermes-agent
that referenced
this pull request
Sep 3, 2026
…parse (NousResearch#60591) Port from openai/codex#31188: a parse failure in a policy-bearing config file must not silently replace the effective policy with an empty/default one. Codex's load_exec_policy_with_warning replaced the whole exec policy with Policy::empty() when a .rules file failed to parse, silently dropping managed prompt/forbidden rules; the fix preserves the managed policy while still warning. Hermes had the same bug shape in load_config(): a YAML parse error made _load_config_impl() fall through to DEFAULT_CONFIG, dropping every user override — including approvals.deny rules, which are documented to block commands even under --yolo. In a long-running gateway, a user mid-editing config.yaml into broken YAML silently disarmed their own deny rules on the next load. Now, when the process has a last successfully loaded config for that path (_LAST_EXPANDED_CONFIG_BY_PATH), a parse failure keeps serving it (cached under the corrupt file's signature so the broken file isn't re-parsed) and the warning says edits are being ignored until the YAML is fixed. Fresh processes with no last-known-good keep the existing DEFAULT_CONFIG fallback and warning. E2E-verified: deny rule 'curl*evil.com*' still blocks after mid-process corruption; fixed file reloads normally; fresh-process fallback unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
load_config()now retains the last successfully loaded config within a process whenconfig.yamlfails to parse, instead of silently falling back toDEFAULT_CONFIG— which dropped every user override, includingapprovals.denysecurity rules that are documented to block commands even under--yolo.Port of openai/codex#31188 ("Preserve managed exec policy after rules parse errors"). Codex's invariant: a parse failure in a policy-bearing config file must not replace the effective policy with an empty one. Their
load_exec_policy_with_warningswapped the whole exec policy forPolicy::empty()when a.rulesfile failed to parse, silently dropping managed prompt/forbidden rules. Hermes had the exact same bug shape in_load_config_impl().The Hermes symptom: a long-running gateway whose user mid-edits
config.yamlinto broken YAML silently disarms their ownapprovals.denyrules (and every other override — fallback chain, aux providers, model settings) on the nextload_config()cache miss. Reproduced live before the fix: withdeny: ['curl*evil.com*']loaded, corrupting the file mid-process made_match_user_deny_rule("curl http://evil.com/x")returnNone.Changes
hermes_cli/config.py_load_config_impl(): on parse failure, if_LAST_EXPANDED_CONFIG_BY_PATHholds a previously loaded config for this path, keep serving it. The retained config is cached under the corrupt file's signature so the broken file isn't re-parsed on every load; fixing the file changes the signature and triggers a normal reload. Fresh processes with no last-known-good keep the existingDEFAULT_CONFIGfallback unchanged._warn_config_parse_failure(): newfallback=wording selector — the warning now says "Keeping the previously loaded config for this process — edits to config.yaml are being IGNORED until the YAML is fixed" when last-known-good is in play, instead of the misleading "Falling back to default config". Backup-to-.bakbehavior unchanged.tests/hermes_cli/test_config.py(retention incl. deny rules, recovery after fix, fresh-process default fallback preserved, no re-warn spam on cached loads).Adaptation notes
_LAST_EXPANDED_CONFIG_BY_PATHalready tracked for env-ref template preservation — no new state added.codex exec); Hermes has no strict-client analog — all callers get the warn-and-retain behavior.save_config()stores the pre-expansion normalized dict in_LAST_EXPANDED_CONFIG_BY_PATH, so the retained value is passed through_expand_env_varsdefensively (idempotent when already expanded).Validation
None(rule silently dropped)curl*evil.com*test_config.py; 218 passed across config+deny-rule suites; 414 passed sibling_warn_config_parse_failureconsumersE2E (real imports, temp
HERMES_HOME, real file I/O): 4/4 scenarios pass.Infographic