{"record":{"id":"4d5625349499d33e","repo":"unclecode/crawl4ai","slug":"task-not-found","errorCode":null,"errorMessage":"Task not found","messagePattern":"Task not found","errorType":"http","errorClass":"HTTPException","httpStatus":404,"severity":"warning","filePath":"deploy/docker/api.py","lineNumber":497,"sourceCode":"\nasync def handle_task_status(\n    redis: aioredis.Redis,\n    task_id: str,\n    base_url: str,\n    *,\n    keep: bool = False,\n    requester: Optional[str] = None,\n    is_admin: bool = False,\n) -> JSONResponse:\n    \"\"\"Handle task status check requests.\n\n    Enforces ownership: a task records the `owner` (principal sub) that created\n    it; a different requester gets 404 (not 403, so task existence is not\n    revealed). Admin-scope principals may read any task.\n    \"\"\"\n    task = await redis.hgetall(f\"task:{task_id}\")\n    if not task:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=\"Task not found\"\n        )\n\n    task = decode_redis_hash(task)\n\n    owner = task.get(\"owner\")\n    if owner and not is_admin and owner != requester:\n        # Do not leak existence of someone else's task.\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=\"Task not found\"\n        )\n\n    response = create_task_response(task, task_id, base_url)\n\n    if task[\"status\"] in [TaskStatus.COMPLETED, TaskStatus.FAILED]:\n        if not keep and should_cleanup_task(task[\"created_at\"]):","sourceCodeStart":479,"sourceCodeEnd":515,"githubUrl":"https://github.com/unclecode/crawl4ai/blob/7e801521428ee12509994d39151006f64055ebe3/deploy/docker/api.py#L479-L515","documentation":"HTTP 404 'Task not found' from the task-status handler (deploy/docker/api.py:497) when Redis hgetall(f\"task:{task_id}\") returns an empty hash — the task key does not exist. Common causes: task already completed and auto-cleaned, TTL expired, wrong/typo'd task_id, or Redis flushed/restarted without persistence. It is deliberately indistinguishable from the ownership 404 at 507.","triggerScenarios":"Polling GET /task/{id} after the task finished and should_cleanup_task deleted it (server deletes completed/failed tasks on read when keep=False and age exceeds threshold); polling long after submission so TTL expired; using a task_id from a previous server/Redis instance.","commonSituations":"Clients that poll infrequently and miss the terminal state before cleanup; Redis restart without volume; race between two pollers where one read triggers deletion and the other then 404s.","solutions":["Poll frequently enough to observe the terminal status before auto-cleanup, or request keep=True so the record persists.","Treat 404 on a previously-seen task id as 'finished & reaped' — record the last observed status client-side.","Persist Redis (appendonly/volume) so task keys survive restarts.","Double-check the task_id is exactly the one returned at submission."],"exampleFix":"# before\nstatus = client.get(f\"{base}/task/{task_id}\").json()\n\n# after\nlast = {}\nwhile True:\n    r = client.get(f\"{base}/task/{task_id}\")\n    if r.status_code == 404:\n        print(\"final:\", last or \"unknown (reaped before first poll)\")\n        break\n    last = r.json()\n    if last[\"status\"] in (\"completed\", \"failed\"):\n        break\n    time.sleep(2)","handlingStrategy":"retry","validationCode":"def task_id_plausible(task_id: str) -> bool:\n    return isinstance(task_id, str) and 8 <= len(task_id) <= 64 and task_id.isalnum()","typeGuard":null,"tryCatchPattern":"r = await client.get(f\"/task/{task_id}\")\nif r.status_code == 404:\n    if task_id in seen_task_ids:\n        return last_status[task_id]      # finished & reaped earlier\n    raise TaskLostError(task_id)          # never existed / wrong id","preventionTips":["Poll at short intervals until terminal status so cleanup never outruns you","Pass keep=true for tasks you must audit later","Persist the last observed status client-side for every task_id","Run Redis with persistence enabled so task records survive restarts"],"tags":["http-404","task-status","redis","ttl","polling"],"backgroundTag":null,"analyzedSha":"7e801521428ee12509994d39151006f64055ebe3","analyzedAt":"2026-08-14T20:46:20.673Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}