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

  1. Treat 404 from cancel as benign idempotent success — the stream is already gone, nothing to cancel
  2. Guard the UI: only show/enable the cancel control while the SSE connection for that request_id is open
  3. Ensure cancel requests reach the same worker as the stream (sticky routing or single worker for agent endpoints)
  4. 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

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.