Skip to content

[Bug] gitt config set destroys every other config key when existing config.json is corrupt JSON #845

Description

@ebios-star

Description

`gitt config set` (gittensor/cli/main.py:117-138) prints a yellow warning when it cannot parse the existing `~/.gittensor/config.json`, then silently overwrites the file with only the new key, losing every previously-configured value:

```python
config = {}
if CONFIG_FILE.exists():
try:
config = json.loads(CONFIG_FILE.read_text())
except json.JSONDecodeError:
console.print('[yellow]Warning: Existing config was invalid, starting fresh[/yellow]')

Set the value

old_value = config.get(key)
config[key] = value

Write config

CONFIG_FILE.write_text(json.dumps(config, indent=2))
```

This is the write-side counterpart to #816 ("`load_config` silently swallows JSONDecodeError"). #816 / PR #817 fix the read paths so commands stop silently retargeting mainnet, but `config_set` still happily clobbers the only on-disk record. After a single corrupt-config recovery, the operator's `network` / `contract_address` / `hotkey` / `ws_endpoint` are gone and the next `gitt issues …` command falls through to finney mainnet defaults.

This is the same family as #781 (`pat_storage._read_file` returning `[]` and the next `save_pat` overwriting the corrupt file) — a write-path that interprets "unparsable" as "empty" instead of "refuse to overwrite".

Steps to Reproduce

  1. `gitt config set network test`
  2. `gitt config set contract_address 5XYZ...`
  3. `gitt config set wallet alice` ← three keys committed.
  4. Corrupt the file (simulating a partial write from a crashed process):
    `echo '{"network": "test"' > ~/.gittensor/config.json`
  5. `gitt config set hotkey default`
  6. `cat ~/.gittensor/config.json` → `{"hotkey": "default"}`. The other three keys are gone.

Expected Behavior

Refuse to overwrite a corrupt config file. Either:

Either way, the operator's previously-set keys must survive a single corrupt-file event.

Actual Behavior

The yellow "Warning: Existing config was invalid, starting fresh" message is printed, but `config = {}` is the actual default, and the subsequent `CONFIG_FILE.write_text(json.dumps(config, indent=2))` writes a fresh single-entry config to disk, losing every other key.

Impact

  • Silent data loss of operator config — `network`, `contract_address`, `ws_endpoint` may all disappear. The next `gitt issues …` runs on finney mainnet against the hardcoded contract instead of the operator's intended target.
  • Severity: high. Mirrors the data-loss class of [Bug] pat_storage._read_file returns [] on corrupt JSON — next save_pat overwrites and permanently loses PATs #781 but for the CLI config rather than validator PATs.
  • Frequency: corrupt JSON files are caused by interrupted writes (`Ctrl-C` mid-`gitt config set`), simultaneous edits, on-disk corruption, or accidental manual edits — none rare.

Suggested fix

Mirror PR #817: turn the JSONDecodeError branch into an error + `SystemExit(1)`. The yellow warning + write-anyway path must go away. A regression test under a new `TestConfigSetCorruption` class in `tests/cli/test_config_set.py` (alongside #813) can pin both the exit code and the on-disk preservation invariant.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions