Skip to content

fix(cli): use atomic write in save_config_value to prevent config loss on interrupt#4276

Closed
binhnt92 wants to merge 1 commit into
NousResearch:mainfrom
binhnt92:fix/cli-save-config-atomic-write
Closed

fix(cli): use atomic write in save_config_value to prevent config loss on interrupt#4276
binhnt92 wants to merge 1 commit into
NousResearch:mainfrom
binhnt92:fix/cli-save-config-atomic-write

Conversation

@binhnt92

Copy link
Copy Markdown
Contributor

save_config_value() uses bare open(config_path, 'w') + yaml.dump() to write config.yaml. The 'w' mode truncates the file to zero bytes immediately on open, before any data is written. If the process is interrupted between truncation and completion of yaml.dump(), config.yaml is left empty — all user configuration (API keys, model settings, preferences) is gone.

The gateway config write path already uses atomic_yaml_write() (temp file + fsync + os.replace) but the CLI path was missed. Every /set, /reasoning, /thinking, /personality, and /skin command goes through save_config_value().

Changes Made

Replaced the bare open(path, 'w') + yaml.dump() in save_config_value() with the existing atomic_yaml_write() utility from utils.py.

How to Test

python3 -m pytest tests/test_save_config_atomic.py -v

5 tests: atomic write preserves existing keys, creates new files, handles existing files, source verification (no bare open('w'), atomic_yaml_write present).

Checklist

  • Tests added (5 tests)
  • Full test suite run — no regressions
  • Tested on Linux (Ubuntu 22.04)

…s on interrupt

save_config_value() uses bare open(config_path, 'w') + yaml.dump() to
write config.yaml. The 'w' mode truncates the file to zero bytes
immediately on open, before any data is written. If the process is
interrupted (Ctrl+C, OOM kill, power loss) between truncation and
completion of yaml.dump(), config.yaml is left empty — losing all user
configuration including API keys, model settings, and preferences.

The gateway config write path was already fixed to use atomic_yaml_write()
(temp file + fsync + os.replace) but the CLI path was missed. Every
/set, /reasoning, /thinking, /personality, and /skin command goes
through save_config_value().
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #4320. Your fix was cherry-picked onto current main with your authorship preserved in git log. Tests were replaced with ones that test save_config_value() directly (mock verification + functional tests) rather than source-code parsing. Thanks for the catch — save_config_value() was the only remaining non-atomic config writer!

@teknium1 teknium1 closed this Mar 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants