Skip to content

fix(export): Guard _fail() against deleted row crash (TOCTOU) - #121

Open
Vigneshselvaraj1811 wants to merge 2 commits into
frappe:mainfrom
Vigneshselvaraj1811:fix/export-fail-toctou
Open

fix(export): Guard _fail() against deleted row crash (TOCTOU)#121
Vigneshselvaraj1811 wants to merge 2 commits into
frappe:mainfrom
Vigneshselvaraj1811:fix/export-fail-toctou

Conversation

@Vigneshselvaraj1811

Copy link
Copy Markdown

Same bug as BUG-1 in migration.py (fixed in 0282b10) but in export.py.

_fail() at atlas/atlas/export.py:475 calls frappe.get_doc("Virtual Machine Image Export", name) without a DoesNotExistError guard. If the row is deleted between the reconcile loop fetching names and processing them, the unhandled exception aborts the entire for name in names loop, skipping all other in-flight exports.

Fix: Wrap frappe.get_doc() in try/except frappe.DoesNotExistError and return early, matching the pattern already applied to migration.py:1209.

Reference: See llm/bugs-and-flows.md BUG-1 for the original bug and its fix.

_reconcile_one() catches all exceptions and calls _fail() to mark the
migration Failed. _fail() then calls frappe.get_doc() — the same call
that may have just failed if the row was deleted. If the row was removed
between the cron fetching names and processing them, the second get_doc
raises DoesNotExistError unhandled, aborting the entire
reconcile_migrations loop and stalling every other in-flight migration.

Fix: catch frappe.DoesNotExistError around the frappe.get_doc() call
instead of a separate db.exists() check — atomic, no TOCTOU window.
Same bug as BUG-1 in migration.py (fixed in 0282b10). If the export
row is deleted between fetching the list and processing it,
frappe.get_doc() raises DoesNotExistError unhandled, aborting the
entire reconcile loop.

Closes frappe#120
@greptile-apps

greptile-apps Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

The export.py fix is straightforward and correct; the migration.py change looks fine in isolation but conflicts with the PR description's claim that it was already patched.

Both changes are logically sound, but the migration.py hunk appears unexpectedly given the PR description — a quick author confirmation is worthwhile before merging.

atlas/atlas/migration.py — verify the _fail() guard addition is intentional and not a stale or accidental include.

Reviews (1): Last reviewed commit: "fix(export): Guard _fail() against delet..." | Re-trigger Greptile

Comment thread atlas/atlas/migration.py
Comment on lines 1209 to 1221
def _fail(name: str, message: str) -> None:
"""Mark a migration Failed, recording the phase it failed at so retry() resumes
there. Best-effort and self-committing (it runs after a rollback)."""
doc = frappe.get_doc("Virtual Machine Migration", name)
try:
doc = frappe.get_doc("Virtual Machine Migration", name)
except frappe.DoesNotExistError:
# The row was deleted between the time we fetched the list of non-terminal
# migrations and now — nothing to fail. This is not an error (the operator
# intentionally removed it), so just return.
return
doc.db_set({"status": "Failed", "error_message": message[-2000:], "error_at_status": doc.status})
# nosemgrep: frappe-manual-commit -- persist the failure so the next tick sees it
frappe.db.commit()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 PR description contradicts the diff — the description states this guard was "already applied to migration.py:1209" and references commit 0282b10, but the diff adds it here for the first time. Either the description is wrong, or this change is unintentional (e.g., a rebase that brought in an uncommitted patch). Worth confirming this is deliberate and not an accidental double-application.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

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