HKUDS/DeepTutor · warning · HTTPException
not active
Error message
not active
What it means
HTTP 409 from POST /memory/runs/{run_id}/cancel when the run manager's cancel() returns falsy — the run exists but is not in a cancellable (active) state. Runs that already completed, failed, or were cancelled cannot be cancelled again.
Source
Thrown at deeptutor/api/routers/memory.py:369
@router.get("/runs/{run_id}")
async def get_run(run_id: str):
from deeptutor.services.memory.consolidator.runs import get_run_manager
run = get_run_manager().get(run_id)
if run is None:
raise HTTPException(status_code=404, detail="unknown run_id")
return run.to_dict()
@router.post("/runs/{run_id}/cancel")
async def cancel_run(run_id: str):
from deeptutor.services.memory.consolidator.runs import get_run_manager
ok = await get_run_manager().cancel(run_id)
if not ok:
raise HTTPException(status_code=409, detail="not active")
return {"run_id": run_id, "cancelled": True}
@router.post("/runs/{run_id}/undo")
async def undo_run_edit(run_id: str):
from deeptutor.services.memory.consolidator.runs import (
RunBusyError,
get_run_manager,
)
manager = get_run_manager()
try:
event = await manager.undo_last(run_id)
except KeyError:
raise HTTPException(status_code=404, detail="unknown run_id")
except RunBusyError as exc:
raise HTTPException(status_code=409, detail=str(exc))
if event is None:View on GitHub (pinned to 3e82f13042)
Solutions
- Treat 409 as 'already terminal': re-fetch GET /runs/{run_id} and update UI to the run's final state
- Guard cancel buttons with the run status and disable once state is not active
- Idempotency-check client-side before re-sending cancel requests
Example fix
// before
await fetch(`/memory/runs/${id}/cancel`, {method:'POST'});
// after
const run = await (await fetch(`/memory/runs/${id}`)).json();
if (run.state === 'running' || run.state === 'pending') {
await fetch(`/memory/runs/${id}/cancel`, {method:'POST'});
} Defensive patterns
Strategy: validation
Validate before calling
const run = await getRun(id); if (!['running','pending'].includes(run.state)) skipCancel();
Type guard
function isCancellable(state: string): boolean { return state === 'running' || state === 'pending'; } Try / catch
try { await cancelRun(id); } catch (e) { if (e.status === 409) refreshRunState(id); else throw e; } Prevention
- Gate cancel on the polled run state
- Disable cancel once state is terminal
- Debounce cancel button clicks
When it happens
Trigger: Cancelling a run that already finished or was previously cancelled; a race where the run completes between the client's status check and the cancel request.
Common situations: UI showing a stale 'running' badge while the run finished, retry storms from an aggressive cancel loop, or double-clicking a cancel button.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
AI-assisted analysis of HKUDS/DeepTutor@3e82f13042 (2026-08-27).
Data as JSON: /api/errors/d65d4ba70b523fd2.
Report an issue: GitHub.