Skip to content

fix: concurrent runs corrupt history.json, and a corrupt one stops startup - #60

Merged
abrignoni merged 1 commit into
mainfrom
fix/history-concurrent-writes
Aug 9, 2026
Merged

fix: concurrent runs corrupt history.json, and a corrupt one stops startup#60
abrignoni merged 1 commit into
mainfrom
fix/history-concurrent-writes

Conversation

@abrignoni

@abrignoni abrignoni commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Two defects in the run-history file, neither platform specific. Levelled across all five cores so they stay in step.

1. Concurrent runs corrupt history.json

_atomic_write_json writes to a temp file and then os.replaces it, which is correct on its own. But every process uses the same temp path, history.tmp. Run two parses at once and they interleave into that one file, so the document that lands in history.json can be two documents concatenated, and whichever process replaces second fails with No such file or directory: history.tmp.

The temp path is now unique per process.

2. A corrupt history.json stops the tool parsing evidence

_read_json catches only OSError. A JSONDecodeError propagates out of startup and kills the run before any artifact executes:

File "leapp_functions/app/history.py", line 252, in _record_path
    history_data = _read_json(get_history_path())
json.decoder.JSONDecodeError: Extra data: line 149 column 2

A damaged convenience file should never prevent evidence being parsed. It now logs, preserves the file as .corrupt.bak, and continues with defaults.

How this surfaced

Running five parses in parallel against a corpus. The history file was left holding two concatenated JSON documents, and every subsequent run then died at startup with the traceback above until the file was moved aside. A history.json.corrupt.bak dated 27 June was already sitting next to it, so this had happened before.

Verification (live, on real runs)

Six simultaneous runs against a deliberately corrupted history.json, three rounds, each killed once past the history write. Everything relevant happens at startup, so there is no need to let parsing finish:

original fixed
runs 18 18
reached parsing 0 18
startup crashes 18 0
history left corrupt 3/3 0/3
stray temp files 0 0

The damaged file is preserved as history.json.corrupt.bak rather than discarded.

Levelled across the cores

Same fix in all five, byte-identical files so they do not drift: iLEAPP #1915, ALEAPP #1080, RLEAPP #400, VLEAPP #116.

Co-Authored-By: Claude Opus 5 noreply@anthropic.com

…artup

Every process wrote to the same history.tmp, so parallel runs interleaved into
it and the replace raced. The temp path is now unique per process.

_read_json caught only OSError, so a JSONDecodeError from a damaged file
propagated out of startup and stopped the tool parsing evidence. It now logs,
keeps the file as .corrupt.bak, and continues.

Verified with 180 concurrent writes from 6 processes: valid JSON, no leftover
temp files. Levelled from iLEAPP so the five cores stay identical.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant