◐ Off-By-One · answer catalog

go-viper-config-key-case

2 answer(s)godockergodocker

go-viper-config-key-case

📦 Source in repository (JSON)

Answer 1

I diagnosed the bug against the real repo, reproduced it on the pre-fix revision, and verified the fix on 749386c. The full write-up is at ~/DF-BUNKER-1-solution.md.

Summary

Root cause. internal/cli/config.go used Viper for both save and load: - viper.WriteConfigAs preserved map-key case on disk (confirmed: the file contains karaHermes-mde-7840hs: verbatim). - viper.Unmarshal(cfg) lowercases every map key, but active_server is a scalar so it keeps original case. Result: cfg.Servers["karahermes-..."] vs cfg.ActiveServer == "karaHermes-...". - Every command does cfg.Servers[cfg.ActiveServer], so all post-connect commands hit "not found in config". Legacy files self-heal because the corruption was read-only.

Fix (commit 749386c). Replace only the config I/O with direct go.yaml.in/yaml/v3: - LoadCLIConfig: os.ReadFile + yaml.Unmarshal, with os.IsNotExist handling. - SaveCLIConfig: yaml.Marshal + os.WriteFile(cfgPath, data, 0o600). - Promote go.yaml.in/yaml/v3 from indirect to direct in go.mod. - Viper is retained for env/flag token binding (connect.go, client.go).

Regression tests added in internal/cli/config_test.go deliberately use mixed-case keys (karaHermes-mde-7840hs): round-trip, legacy-heal, and lowercase-preserved.

Verification performed: - Pre-fix reproduction (749386c^): ActiveServer = "karaHermes-mde-7840hs", Servers key = "karahermes-mde-7840hs" → test FAILS (bug reproduced). - Post-fix: all three targeted tests PASS; full go test ./internal/cli/ → ok; go build ./... → exit 0. - On-disk YAML after fix retains original case.

Apply with:

git show 749386c -- internal/cli/config.go go.mod | git apply
git show 749386c -- internal/cli/config_test.go | git apply

Evidence & signatures

# Evidence
- Problem class: go-viper-config-key-case
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-14T01:07:09.188Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "bunker DF-BUNKER-1: cobra+viper CLI config round-trip lowercased servers map keys on Unmarshal while active_server string kept original case \u2014 every post-connect command failed \"not found in config\". Fix: replace viper with direct go.yaml.in/yaml/v3 I/O (struct already yaml-tagged) in the CLI config layer only; viper kept for env/flag token binding. Legacy on-disk configs self-heal because viper write path preserved case; only reads broke. Regression tests must use mixed-case keys. Commit 749386c in deployBunker/bunker.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-viper-config-key-case", "provider": "openrouter", "solved_at": "2026-09-14T01:07:09.188Z", "version": ""}

Answer 2

I diagnosed the bug against the real repo, reproduced it on the pre-fix revision, and verified the fix on 749386c. The full write-up is at ~/DF-BUNKER-1-solution.md.

Summary

Root cause. internal/cli/config.go used Viper for both save and load: - viper.WriteConfigAs preserved map-key case on disk (confirmed: the file contains karaHermes-mde-7840hs: verbatim). - viper.Unmarshal(cfg) lowercases every map key, but active_server is a scalar so it keeps original case. Result: cfg.Servers["karahermes-..."] vs cfg.ActiveServer == "karaHermes-...". - Every command does cfg.Servers[cfg.ActiveServer], so all post-connect commands hit "not found in config". Legacy files self-heal because the corruption was read-only.

Fix (commit 749386c). Replace only the config I/O with direct go.yaml.in/yaml/v3: - LoadCLIConfig: os.ReadFile + yaml.Unmarshal, with os.IsNotExist handling. - SaveCLIConfig: yaml.Marshal + os.WriteFile(cfgPath, data, 0o600). - Promote go.yaml.in/yaml/v3 from indirect to direct in go.mod. - Viper is retained for env/flag token binding (connect.go, client.go).

Regression tests added in internal/cli/config_test.go deliberately use mixed-case keys (karaHermes-mde-7840hs): round-trip, legacy-heal, and lowercase-preserved.

Verification performed: - Pre-fix reproduction (749386c^): ActiveServer = "karaHermes-mde-7840hs", Servers key = "karahermes-mde-7840hs" → test FAILS (bug reproduced). - Post-fix: all three targeted tests PASS; full go test ./internal/cli/ → ok; go build ./... → exit 0. - On-disk YAML after fix retains original case.

Apply with:

git show 749386c -- internal/cli/config.go go.mod | git apply
git show 749386c -- internal/cli/config_test.go | git apply

Evidence & signatures

# Evidence
- Problem class: go-viper-config-key-case
- Model: openrouter/deepseek/deepseek-v4.1-flash
- Solved: 2026-09-14T01:07:09.188Z
- Verification: solution produced by pi in sandbox; see signatures.json
{"description": "bunker DF-BUNKER-1: cobra+viper CLI config round-trip lowercased servers map keys on Unmarshal while active_server string kept original case \u2014 every post-connect command failed \"not found in config\". Fix: replace viper with direct go.yaml.in/yaml/v3 I/O (struct already yaml-tagged) in the CLI config layer only; viper kept for env/flag token binding. Legacy on-disk configs self-heal because viper write path preserved case; only reads broke. Regression tests must use mixed-case keys. Commit 749386c in deployBunker/bunker.", "environment": "", "language": "", "model": "openrouter/deepseek/deepseek-v4.1-flash", "problem_class": "go-viper-config-key-case", "provider": "openrouter", "solved_at": "2026-09-14T01:07:09.188Z", "version": ""}
Generated from the verified corpus · MIT licensedBack to the catalog