{"record":{"id":"1b8dcdef3ff77aa2","repo":"langflow-ai/langflow","slug":"event-polling-was-cancelled","errorCode":null,"errorMessage":"Event polling was cancelled","messagePattern":"Event polling was cancelled","errorType":"http","errorClass":"HTTPException","httpStatus":499,"severity":"info","filePath":"src/backend/base/langflow/api/build.py","lineNumber":315,"sourceCode":"                events.append(_project_event_to_v1(value.decode(\"utf-8\")))\n\n            # If no events were available, wait for one (with timeout)\n            if not events:\n                _, value, _ = await main_queue.get()\n                if value is None:\n                    # End of stream, trigger end event\n                    if event_task is not None:\n                        event_task.cancel()\n                    event_manager.on_end(data={})\n                else:\n                    events.append(_project_event_to_v1(value.decode(\"utf-8\")))\n\n            # Return as NDJSON format - each line is a complete JSON object\n            content = \"\\n\".join([event for event in events if event is not None])\n            return Response(content=content, media_type=\"application/x-ndjson\")\n        except asyncio.CancelledError as exc:\n            await logger.ainfo(f\"Event polling was cancelled for job {job_id}\")\n            raise HTTPException(status_code=499, detail=\"Event polling was cancelled\") from exc\n        except asyncio.TimeoutError:\n            await logger.awarning(f\"Timeout while waiting for events for job {job_id}\")\n            return Response(content=\"\", media_type=\"application/x-ndjson\")  # Return empty response instead of error\n\n    except JobQueueNotFoundError as exc:\n        await logger.aerror(f\"Job not found: {job_id}. Error: {exc!s}\")\n        raise HTTPException(status_code=404, detail=f\"Job not found: {exc!s}\") from exc\n    except Exception as exc:\n        if isinstance(exc, HTTPException):\n            raise\n        await logger.aexception(f\"Unexpected error processing flow events for job {job_id}\")\n        raise HTTPException(status_code=500, detail=f\"Unexpected error: {exc!s}\") from exc\n\n\nasync def create_flow_response(\n    queue: asyncio.Queue,\n    event_manager: EventManager,\n    event_task: asyncio.Task | None,","sourceCodeStart":297,"sourceCodeEnd":333,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/build.py#L297-L333","documentation":"Raised when the polling loop for build events is cancelled via asyncio.CancelledError — almost always because the HTTP client disconnected while the server was waiting for the next event batch. It is mapped to status 499 (nginx-style 'client closed request') and logged at info level, confirming it is an expected lifecycle event, not a fault.","triggerScenarios":"Using event_delivery=polling on the events endpoint and the client (browser tab closed, curl interrupted, SDK timeout) drops the connection before the stream ends; server shutdown cancelling the handler task.","commonSituations":"Frontend StrictMode double-mounts aborting the first poll; aggressive client timeouts shorter than the build duration; users navigating away mid-build.","solutions":["No server fix needed — 499 here is informational; the job keeps running in the queue.","On the client, raise the request timeout to exceed expected build time, or switch to streaming delivery.","Re-attach with the same job_id to resume consuming events instead of restarting the build."],"exampleFix":"// before (client aborts early)\nconst res = await fetch(url, { signal: AbortSignal.timeout(2000) });\n\n// after\nconst res = await fetch(url, { signal: AbortSignal.timeout(300_000) });","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    events = await poll_events(job_id)\nexcept HTTPError as e:\n    if e.response.status_code == 499:\n        events = await poll_events(job_id)  # job still runs; just re-attach\n    else:\n        raise","preventionTips":["Set client timeouts longer than worst-case build time when polling.","Prefer streaming delivery for long builds to avoid long-held polling requests.","On disconnect, resume with the same job_id instead of restarting the build."],"tags":["http-499","polling","client-disconnect","cancellation"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}