ZhuLinsen/daily_stock_analysis · info · HTTPException
request_not_active
request_not_active
Error message
This Agent request is no longer running
What it means
POST /agent/chat/stream/{request_id}/cancel returns HTTP 404 code=request_not_active when no active Codex SSE stream is registered under that request_id. The lookup (api/v1/endpoints/agent.py:666-669) reads _ACTIVE_CODEX_STREAMS under lock; an entry only exists while a codex_app_server chat stream is open. Cancellation is cooperative: the endpoint sets the cancel_event which the stream's progress_callback checks.
Source
Thrown at api/v1/endpoints/agent.py:669
return StreamingResponse(
event_generator(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"X-Accel-Buffering": "no",
"Connection": "keep-alive",
},
)
@router.post("/chat/stream/{request_id}/cancel")
async def cancel_agent_chat_stream(request_id: str):
"""Signal cancellation while the original Codex SSE remains open."""
with _ACTIVE_CODEX_STREAMS_LOCK:
cancel_event = _ACTIVE_CODEX_STREAMS.get(request_id)
if cancel_event is None:
raise HTTPException(
status_code=404,
detail={
"error": "request_not_active",
"message": "This Agent request is no longer running",
},
)
cancel_event.set()
return {"accepted": True, "request_id": request_id}
View on GitHub (pinned to 5159bd72e8)
Solutions
- Treat 404 from cancel as benign idempotent success — the stream is already gone, nothing to cancel
- Guard the UI: only show/enable the cancel control while the SSE connection for that request_id is open
- Ensure cancel requests reach the same worker as the stream (sticky routing or single worker for agent endpoints)
- Log request_id correlation client-side so mismatches between started and cancelled ids are obvious
Example fix
// before
await fetch(`/api/v1/agent/chat/stream/${id}/cancel`, {method:'POST'}); // throws on 404
// after
const res = await fetch(`/api/v1/agent/chat/stream/${id}/cancel`, {method:'POST'});
if (res.status === 404) {
// stream already ended; nothing to cancel
return { alreadyStopped: true };
}
if (!res.ok) throw new Error('cancel failed'); Defensive patterns
Strategy: try-catch
Try / catch
try:
resp = post_cancel(request_id)
except HTTPError as e:
if e.response.status_code == 404 and 'request_not_active' in e.response.text:
pass # already finished; treat as success (idempotent cancel)
else:
raise Prevention
- Only expose the cancel control while the SSE connection for that request_id is open
- Treat cancel as idempotent by design — 404 is the 'already stopped' answer, not an error
- Correlate request_id between stream start and cancel calls client-side
When it happens
Trigger: Cancelling after the stream already completed normally (race between UI stop button and stream end); cancelling a request_id that was never started or was mistyped; cancelling a stream on a different backend (non-codex backends never register entries); cancelling against a different server process/worker than the one holding the stream (registry is per-process, agent.py:47).
Common situations: Stop button in the web/desktop client firing after the SSE already closed; retrying a cancel that succeeded; deployments with multiple uvicorn workers where the cancel call lands on a worker that does not own the stream; delayed cancel after a server restart cleared the in-memory dict.
Related errors
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/1d3e2944890ca2c4.
Report an issue: GitHub.