fix: prevent duplicate YAML keys in Hermes config#3267
Conversation
Three changes in hermes_config.rs: 1. deduplicate_top_level_keys() - scan and remove duplicate top-level keys before YAML parsing, preventing "duplicate entry" parse errors 2. remove_all_sections() - helper to strip all occurrences of a given top-level key from raw YAML text 3. replace_yaml_section() now calls remove_all_sections() on the remainder after replacing the primary occurrence, preventing duplicate sections from accumulating on repeated writes Fixes the issue where mcp_servers (or any top-level key) gets duplicated in config.yaml, causing "Failed to parse Hermes config as YAML: duplicate entry with key" errors. Co-Authored-By: que3sui <[email protected]>
4101154 to
d12bd21
Compare
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d12bd21162
ℹ️ 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".
is_top_level_key_line only accepted empty, space, or tab after the colon,
but deduplicate_top_level_keys uses split_inclusive('\n'), so lines end
with \n (LF) or \r\n (CRLF). Without accepting \r and \n as valid
post-colon characters, the dedup safety net never activates.
Add \r and \n checks to is_top_level_key_line, and three tests covering
LF, CRLF, and first-occurrence preservation.
Co-Authored-By: Claude Opus 4.7 <[email protected]>
|
Confirmed this still affects the Windows v3.16.2 release in a real Hermes setup. The config had mostly CRLF line endings, so I tested a local fix that:
Validation on current The recovery behavior may be worth including alongside the CRLF prevention fix, because affected users currently cannot edit/switch providers once |
|
在 Windows 11 + CC Switch v3.16.2(便携版)+ Hermes Agent 上确认复现,这个 PR 正好修复了我们今天遇到的问题。 复现情况:
这与 #3633 的根因分析一致: 本 PR 当前版本( |
Reworks the healing layers on top of the CRLF root-cause fix: - deduplicate_top_level_keys: keep the LAST occurrence of each duplicated key instead of the first. Duplicates come from section replacement degrading into appends (farion1231#3633), so the last block is the newest data -- and Hermes itself reads the config with PyYAML, whose duplicate-key semantics are last-wins. Keeping the first occurrence would silently roll users back to stale config and diverge from what Hermes runs with. Healthy files take a fast path and are returned untouched. - Drop the unused dup_key variable (fails cargo clippy -- -D warnings, which CI enforces). - replace_yaml_section: clean residual duplicate sections from the remainder via remove_all_sections; values come from the keep-last healed read, so dropping all stale on-disk copies loses nothing. - Add regression tests for the actual root cause (find/replace on CRLF input must replace in place, not append), keep-last semantics, identity on healthy files, end-to-end heal-then-parse, and duplicate cleanup on write. Fixes farion1231#3633 farion1231#2973 farion1231#2529 farion1231#3310 farion1231#3762
* fix: prevent duplicate YAML keys in Hermes config Three changes in hermes_config.rs: 1. deduplicate_top_level_keys() - scan and remove duplicate top-level keys before YAML parsing, preventing "duplicate entry" parse errors 2. remove_all_sections() - helper to strip all occurrences of a given top-level key from raw YAML text 3. replace_yaml_section() now calls remove_all_sections() on the remainder after replacing the primary occurrence, preventing duplicate sections from accumulating on repeated writes Fixes the issue where mcp_servers (or any top-level key) gets duplicated in config.yaml, causing "Failed to parse Hermes config as YAML: duplicate entry with key" errors. Co-Authored-By: que3sui <[email protected]> * fix: handle CRLF and LF line endings in top-level key deduplication is_top_level_key_line only accepted empty, space, or tab after the colon, but deduplicate_top_level_keys uses split_inclusive('\n'), so lines end with \n (LF) or \r\n (CRLF). Without accepting \r and \n as valid post-colon characters, the dedup safety net never activates. Add \r and \n checks to is_top_level_key_line, and three tests covering LF, CRLF, and first-occurrence preservation. Co-Authored-By: Claude Opus 4.7 <[email protected]> * refactor(hermes): keep last occurrence when healing duplicate YAML keys Reworks the healing layers on top of the CRLF root-cause fix: - deduplicate_top_level_keys: keep the LAST occurrence of each duplicated key instead of the first. Duplicates come from section replacement degrading into appends (farion1231#3633), so the last block is the newest data -- and Hermes itself reads the config with PyYAML, whose duplicate-key semantics are last-wins. Keeping the first occurrence would silently roll users back to stale config and diverge from what Hermes runs with. Healthy files take a fast path and are returned untouched. - Drop the unused dup_key variable (fails cargo clippy -- -D warnings, which CI enforces). - replace_yaml_section: clean residual duplicate sections from the remainder via remove_all_sections; values come from the keep-last healed read, so dropping all stale on-disk copies loses nothing. - Add regression tests for the actual root cause (find/replace on CRLF input must replace in place, not append), keep-last semantics, identity on healthy files, end-to-end heal-then-parse, and duplicate cleanup on write. Fixes farion1231#3633 farion1231#2973 farion1231#2529 farion1231#3310 farion1231#3762 --------- Co-authored-by: que3sui <[email protected]> Co-authored-by: Claude Opus 4.7 <[email protected]> Co-authored-by: Jason <[email protected]>
Summary
Fixes a bug where top-level YAML keys (typically
mcp_servers) get duplicated in~/.hermes/config.yaml, causing subsequent reads to fail with:Failed to parse Hermes config as YAML: duplicate entry with key "mcp_servers".Root Cause
replace_yaml_section()only replaced the first occurrence. Duplicates in the remainder persisted permanently.Fix (3 changes in
hermes_config.rs)deduplicate_top_level_keys()- removes duplicate keys before YAML parsingremove_all_sections()- strips all occurrences of a given key from raw YAMLreplace_yaml_section()- now cleans duplicates from remainder after replacementTest plan