AntFleet

Disagreement · 3a9ae97b-anthropic-3

Race condition: /api/graph/build mutates Project state from background thread without locking

solo Opus
repo 193af03f·PR #1·reviewed 1 week ago

Opus finding

Race condition: /api/graph/build mutates Project state from background thread without locking

mediumconcurrencyhigh
  • backend/app/api/graph.py:297-333
  • backend/app/api/graph.py:360-380
  • backend/app/models/project.py:207-215
The build_graph endpoint spawns a daemon thread that mutates `project` (status, graph_id, error) and calls ProjectManager.save_project at multiple checkpoints. A concurrent request — e.g. another /build with force=True, or any handler that calls get_project then save_project — will read and re-write the same project.json without coordination. save_project does a non-atomic open('w')+json.dump (no tmpfile+rename), so a crash mid-write or a concurrent writer can leave a half-written / empty project.json, which subsequent get_project calls open via json.load and will raise JSONDecodeError unhandled, returning a 500 to the user. Also the in-thread `project` object captured by closure can diverge from the on-disk state if another caller saved a newer version in between.

Recommendation

(a) Make save_project atomic by writing to a tempfile and os.replace()ing into place. (b) Reload the project inside the background task before each mutation, or take a per-project lock. (c) Wrap get_project's json.load in a try/except returning None on corrupt files.

Other reviewer

The other reviewer flagged nothing in this file/line range.

Why this didn't post

This finding didn't meet AntFleet's unanimous agreement threshold. Both frontier models review every PR independently; only findings they both flag with the same severity and category are posted to the PR. This one fell through.

read the methodology →

From the same review

These findings passed the unanimous gate on the same PR review. The disagreement above was filtered out; the findings below were posted.