HIGH: Receipt OCR background task uses store closed by FastAPI dependency #91

Closed
opened 2026-04-18 09:02:10 -07:00 by pyr0ball · 0 comments
Owner

Summary

The receipt OCR background task (_process_receipt_ocr) receives a store that has already been closed by FastAPI's dependency cleanup before the background task runs. This causes silent failures or crashes during OCR processing.

Root Cause

app/api/endpoints/receipts.py:

@router.post("/", ...)
async def upload_receipt(
    background_tasks: BackgroundTasks,
    file: UploadFile = File(...),
    store: Store = Depends(get_store),  # ← closed after response
    ...
):
    ...
    background_tasks.add_task(_process_receipt_ocr, receipt["id"], saved, store)
    #                                                                       ^^^^
    # FastAPI closes this store in the get_store finally block BEFORE the background task runs.

app/db/session.py:

def get_store(session) -> Generator[Store, None, None]:
    store = Store(session.db)
    try:
        yield store
    finally:
        store.close()  # ← runs before background task

Fix

Open a new Store inside the background task function instead of passing the request-scoped one:

async def _process_receipt_ocr(receipt_id: int, image_path: Path, db_path: Path) -> None:
    store = Store(db_path)
    try:
        await asyncio.to_thread(store.update_receipt_status, receipt_id, "processing")
        service = ReceiptService(store)
        await service.process(receipt_id, image_path)
    except Exception as exc:
        await asyncio.to_thread(store.update_receipt_status, receipt_id, "error", str(exc))
    finally:
        store.close()

Then pass session.db (a Path) to the task instead of the store:

background_tasks.add_task(_process_receipt_ocr, receipt["id"], saved, session.db)

Same fix needed in upload_receipts_batch.

## Summary The receipt OCR background task (`_process_receipt_ocr`) receives a `store` that has already been closed by FastAPI's dependency cleanup before the background task runs. This causes silent failures or crashes during OCR processing. ## Root Cause `app/api/endpoints/receipts.py`: ```python @router.post("/", ...) async def upload_receipt( background_tasks: BackgroundTasks, file: UploadFile = File(...), store: Store = Depends(get_store), # ← closed after response ... ): ... background_tasks.add_task(_process_receipt_ocr, receipt["id"], saved, store) # ^^^^ # FastAPI closes this store in the get_store finally block BEFORE the background task runs. ``` `app/db/session.py`: ```python def get_store(session) -> Generator[Store, None, None]: store = Store(session.db) try: yield store finally: store.close() # ← runs before background task ``` ## Fix Open a new Store inside the background task function instead of passing the request-scoped one: ```python async def _process_receipt_ocr(receipt_id: int, image_path: Path, db_path: Path) -> None: store = Store(db_path) try: await asyncio.to_thread(store.update_receipt_status, receipt_id, "processing") service = ReceiptService(store) await service.process(receipt_id, image_path) except Exception as exc: await asyncio.to_thread(store.update_receipt_status, receipt_id, "error", str(exc)) finally: store.close() ``` Then pass `session.db` (a Path) to the task instead of the store: ```python background_tasks.add_task(_process_receipt_ocr, receipt["id"], saved, session.db) ``` Same fix needed in `upload_receipts_batch`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Circuit-Forge/kiwi#91
No description provided.