Completed BLAST jobs no longer show a stale worker_lost error¶
Motivation¶
A BLAST job whose Run details page showed Status: completed with 3 result
files (7.3 KB) still rendered a red worker_lost line at the bottom of the Job
Details card. A successfully-completed job must not look like it failed.
Root cause is a state-machine cleanup gap, not a runtime failure:
- The job's submit worker was transiently lost (
WorkerLostError) — the worker sidecar was restarted / OOM-killed whileelastic-blast submitwas mid-flight — or the row simply went quiet, so thereconcile_stale_jobsbeat stamped the rowstatus=failed, error_code=worker_lost. - The actual BLAST runtime in AKS kept running independently and produced
results. A later
_reconcile_row_k8s_status/ external refresh / artifact finalize detected completion and flippedstatus+phasetocompleted. - None of those completion paths clear the top-level
error_codecolumn._reconcile_row_k8s_statuspasseserror_code=""only into the payload_progressblock (not the indexed column); the Celery-SUCCESS and external-refresh branches callrepo.update(status=…, phase=…)with noerror_code;finalize_job_artifactsnever touches it. - The job projection
_job_error_for_responseonly suppressederror_codeforstatus == "running", so acompletedrow surfacederror="worker_lost", and the SPA'sshouldShowNonTerminalJobErrorpainted it red.
User-facing change¶
A successfully-completed job (status == "completed") no longer shows a stale
transient error (e.g. worker_lost) on the Run details page. The success banner
remains the terminal representation. Genuinely failed/cancelled jobs are
unchanged — they still surface their error.
API / IaC diff summary¶
api/services/blast/job_state.py—_job_error_for_responsenow returns""whenstatus == "completed"(mirrors the existingrunning+blast_submit_lock_busysuppression). The rawerror_codefield is still emitted for diagnostics; only the user-facingerroris suppressed. This is the single read-path chokepoint, so it also fixes already-stuck terminal rows on the next poll (they are never re-reconciled).web/src/pages/blastResultsModel.ts—shouldShowNonTerminalJobErroradds a terminal-success guard (status === "completed" || phase === "completed"→false) as defense-in-depth so the rule holds even if a caller hands the component a completed job that still carries anerrorstring.- No IaC change. No data migration (display-layer fix).
Validation evidence¶
uv run pytest -q api/tests→ 3203 passed, 3 skipped.- New backend test
test_local_to_blast_job_suppresses_stale_error_on_completed_job. - New frontend test "suppresses a stale error on a successfully-completed job"
(
web/src/pages/blastResultsModel.test.ts, 7 passed). uv run ruff checkclean;npx tsc --noEmitclean.
Noted follow-up (not implemented)¶
The stored row keeps error_code="worker_lost" after completion. The display is
now correct, but for storage hygiene a future change could also clear the
top-level error_code on the three reconcile completion writes
(_reconcile_row_k8s_status completed branch, the Celery-SUCCESS→completed
branch, and the external-refresh→completed branch). Deferred to keep this change
minimal and because it does not affect the user-visible outcome.