Problem
When the worker process dies mid-execution (after setting job status to OCR_STARTED but before the OpenAI batch is submitted), the job is permanently stuck:
provider_job_id remains NULL — no remote batch to poll
- Worker only picks up
NOT_STARTED jobs (worker.py:174)
- Orphan detection only runs at startup (
_detect_orphaned_jobs in worker.py:159) — if the server stays up, the stuck job is never recovered
- The orphan detector terminates stuck jobs to error states rather than retrying them
Reproduction
- Start a job with a real OpenAI provider
- Kill the worker process after job transitions to
OCR_STARTED but before batch submission completes
- If server stays up: job stuck forever in
OCR_STARTED
- If server restarts: orphan detector moves it to
OCR_FAILED (data lost, no retry)
Impact
- Jobs can get stuck in
OCR_STARTED, OCR_COMPLETED, MATCHING_PENDING, or MATCHING states with no recovery path while the server is running
- The only recovery is a server restart, which terminates the job to an error state rather than retrying
Suggested Fix
Add periodic runtime orphan detection to the worker loop — every N seconds check for jobs stuck in non-terminal states beyond a threshold, and either retry (reset to NOT_STARTED) or terminate gracefully.
Alternatively, add a POST /api/jobs/{id}/retry endpoint that resets a stuck job.
Related
Problem
When the worker process dies mid-execution (after setting job status to
OCR_STARTEDbut before the OpenAI batch is submitted), the job is permanently stuck:provider_job_idremainsNULL— no remote batch to pollNOT_STARTEDjobs (worker.py:174)_detect_orphaned_jobsinworker.py:159) — if the server stays up, the stuck job is never recoveredReproduction
OCR_STARTEDbut before batch submission completesOCR_STARTEDOCR_FAILED(data lost, no retry)Impact
OCR_STARTED,OCR_COMPLETED,MATCHING_PENDING, orMATCHINGstates with no recovery path while the server is runningSuggested Fix
Add periodic runtime orphan detection to the worker loop — every N seconds check for jobs stuck in non-terminal states beyond a threshold, and either retry (reset to
NOT_STARTED) or terminate gracefully.Alternatively, add a
POST /api/jobs/{id}/retryendpoint that resets a stuck job.Related
OCR_STARTED