fix(gateway): coerce scalar model: to dict before /model --global persist#32272
Merged
Conversation
…ersist
Reported via AskClaw. When config.yaml has `model: <name>` (flat string)
instead of the nested `model: {default: ..., provider: ...}` form, every
gateway `/model X --global` crashed silently with
TypeError: 'str' object does not support item assignment
The persist block did:
model_cfg = cfg.setdefault("model", {})
model_cfg["default"] = result.new_model
`setdefault` returns the existing scalar, and the next assignment blows
up. The 'switch failed' warning was logged at WARNING level and the user
never saw why their persist didn't stick.
Coerce scalar/None `model:` into a dict before mutation, in both the
gateway path (`gateway/run.py`) and the sister site in
`hermes_cli/doctor.py --fix` (same setdefault-on-string flaw). The CLI
`/model` path is unaffected because it goes through `_set_nested` which
already replaces scalar leaves with dicts.
Regression test `tests/gateway/test_model_command_flat_string_config.py`
covers the flat-string, missing, and proper-dict cases. Without the fix,
the flat-string case fails with the exact original TypeError.
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
unresolved-import |
1 |
First entries
tests/gateway/test_model_command_flat_string_config.py:15: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
✅ Fixed issues: none
Unchanged: 4949 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
bridge25
pushed a commit
to bridge25/hermes-agent
that referenced
this pull request
May 27, 2026
…ersist (NousResearch#32272) Reported via AskClaw. When config.yaml has `model: <name>` (flat string) instead of the nested `model: {default: ..., provider: ...}` form, every gateway `/model X --global` crashed silently with TypeError: 'str' object does not support item assignment The persist block did: model_cfg = cfg.setdefault("model", {}) model_cfg["default"] = result.new_model `setdefault` returns the existing scalar, and the next assignment blows up. The 'switch failed' warning was logged at WARNING level and the user never saw why their persist didn't stick. Coerce scalar/None `model:` into a dict before mutation, in both the gateway path (`gateway/run.py`) and the sister site in `hermes_cli/doctor.py --fix` (same setdefault-on-string flaw). The CLI `/model` path is unaffected because it goes through `_set_nested` which already replaces scalar leaves with dicts. Regression test `tests/gateway/test_model_command_flat_string_config.py` covers the flat-string, missing, and proper-dict cases. Without the fix, the flat-string case fails with the exact original TypeError.
mathias3
pushed a commit
to mathias3/hermes-agent
that referenced
this pull request
May 28, 2026
…ersist (NousResearch#32272) Reported via AskClaw. When config.yaml has `model: <name>` (flat string) instead of the nested `model: {default: ..., provider: ...}` form, every gateway `/model X --global` crashed silently with TypeError: 'str' object does not support item assignment The persist block did: model_cfg = cfg.setdefault("model", {}) model_cfg["default"] = result.new_model `setdefault` returns the existing scalar, and the next assignment blows up. The 'switch failed' warning was logged at WARNING level and the user never saw why their persist didn't stick. Coerce scalar/None `model:` into a dict before mutation, in both the gateway path (`gateway/run.py`) and the sister site in `hermes_cli/doctor.py --fix` (same setdefault-on-string flaw). The CLI `/model` path is unaffected because it goes through `_set_nested` which already replaces scalar leaves with dicts. Regression test `tests/gateway/test_model_command_flat_string_config.py` covers the flat-string, missing, and proper-dict cases. Without the fix, the flat-string case fails with the exact original TypeError.
Bryce-huang
pushed a commit
to wbkunlun/hermes-agent
that referenced
this pull request
May 29, 2026
…ersist (NousResearch#32272) Reported via AskClaw. When config.yaml has `model: <name>` (flat string) instead of the nested `model: {default: ..., provider: ...}` form, every gateway `/model X --global` crashed silently with TypeError: 'str' object does not support item assignment The persist block did: model_cfg = cfg.setdefault("model", {}) model_cfg["default"] = result.new_model `setdefault` returns the existing scalar, and the next assignment blows up. The 'switch failed' warning was logged at WARNING level and the user never saw why their persist didn't stick. Coerce scalar/None `model:` into a dict before mutation, in both the gateway path (`gateway/run.py`) and the sister site in `hermes_cli/doctor.py --fix` (same setdefault-on-string flaw). The CLI `/model` path is unaffected because it goes through `_set_nested` which already replaces scalar leaves with dicts. Regression test `tests/gateway/test_model_command_flat_string_config.py` covers the flat-string, missing, and proper-dict cases. Without the fix, the flat-string case fails with the exact original TypeError. #AI commit#
mosaiq-systems
pushed a commit
to mosaiq-systems/hermes-agent
that referenced
this pull request
May 29, 2026
…ersist (NousResearch#32272) Reported via AskClaw. When config.yaml has `model: <name>` (flat string) instead of the nested `model: {default: ..., provider: ...}` form, every gateway `/model X --global` crashed silently with TypeError: 'str' object does not support item assignment The persist block did: model_cfg = cfg.setdefault("model", {}) model_cfg["default"] = result.new_model `setdefault` returns the existing scalar, and the next assignment blows up. The 'switch failed' warning was logged at WARNING level and the user never saw why their persist didn't stick. Coerce scalar/None `model:` into a dict before mutation, in both the gateway path (`gateway/run.py`) and the sister site in `hermes_cli/doctor.py --fix` (same setdefault-on-string flaw). The CLI `/model` path is unaffected because it goes through `_set_nested` which already replaces scalar leaves with dicts. Regression test `tests/gateway/test_model_command_flat_string_config.py` covers the flat-string, missing, and proper-dict cases. Without the fix, the flat-string case fails with the exact original TypeError.
gweeteve
pushed a commit
to gweeteve/hermes-agent
that referenced
this pull request
Jun 2, 2026
…ersist (NousResearch#32272) Reported via AskClaw. When config.yaml has `model: <name>` (flat string) instead of the nested `model: {default: ..., provider: ...}` form, every gateway `/model X --global` crashed silently with TypeError: 'str' object does not support item assignment The persist block did: model_cfg = cfg.setdefault("model", {}) model_cfg["default"] = result.new_model `setdefault` returns the existing scalar, and the next assignment blows up. The 'switch failed' warning was logged at WARNING level and the user never saw why their persist didn't stick. Coerce scalar/None `model:` into a dict before mutation, in both the gateway path (`gateway/run.py`) and the sister site in `hermes_cli/doctor.py --fix` (same setdefault-on-string flaw). The CLI `/model` path is unaffected because it goes through `_set_nested` which already replaces scalar leaves with dicts. Regression test `tests/gateway/test_model_command_flat_string_config.py` covers the flat-string, missing, and proper-dict cases. Without the fix, the flat-string case fails with the exact original TypeError.
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.
Reported via AskClaw. Telegram/Discord
/model X --globalsilently crashed whenconfig.yamlhadmodel: <name>as a flat string instead of the nestedmodel: {default: ..., provider: ...}form.Root cause
The
Failed to persist model switchwarning was logged at WARNING level and the user just saw 'switch didn't stick'.Same flaw at
hermes_cli/doctor.py:815(doctor --fixpath for migrating root-level keys).How the flat string gets into config:
hermes config set model deepseek-v4-flashwrites a bare scalar via_set_nested's single-segment leaf branch — that's been there since the original config-set command. So the buggy state is reachable through normal CLI usage.The CLI
/model X --globalpath is unaffected because it goes throughsave_config_value("model.default", ...)→_set_nested, which replaces scalar leaves with fresh dicts.Fix
Coerce scalar/None
model:into a dict before mutation, in both call sites:Test
New
tests/gateway/test_model_command_flat_string_config.pycovers three cases:model:deepseek-v4-flashVerified the flat-string test fails on
origin/mainwith the exact original error:WARNING gateway.run:run.py:10447 Failed to persist model switch: 'str' object does not support item assignment.All 25 tests across the 7 gateway model-command test files pass after the fix. Doctor tests (73 across 3 files) also pass.
Infographic