can1357/oh-my-pi · info · HTTPException

delivery {delivery_id} is {event.state}; only running delive

Error message

delivery {delivery_id} is {event.state}; only running deliveries can be cancelled

What it means

FastAPI raises HTTP 409 with 'delivery {id} is {state}; only running deliveries can be cancelled' from POST /api/cancel when the event exists but its recorded state is not 'running'. Cancelling only applies to in-flight tasks; completed, failed, or queued deliveries cannot be cancelled, only retried (inactive states) via /api/trigger retry mode.

Source

Thrown at python/robomp/src/server.py:691

        x_robomp_token: str | None = Header(None, alias="X-Robomp-Replay-Token"),
    ) -> JSONResponse:
        """Stop a running event. The omp subprocess is killed; the row lands in
        `failed` with `cancelled by operator` as the error.
        """
        bag = request.app.state.bag
        cfg: Settings = bag["settings"]
        _require_trigger_token(cfg, x_robomp_token)

        delivery_id = payload.get("delivery_id")
        if not isinstance(delivery_id, str) or not delivery_id:
            raise HTTPException(400, "cancel requires 'delivery_id'")

        db: Database = bag["db"]
        event = db.get_event(delivery_id)
        if event is None:
            raise HTTPException(404, f"unknown delivery {delivery_id}")
        if event.state != "running":
            raise HTTPException(
                409, f"delivery {delivery_id} is {event.state}; only running deliveries can be cancelled"
            )

        pool: WorkerPool = bag["pool"]
        fired = await pool.cancel_event(delivery_id)
        log.info(
            "manual cancel",
            extra={"delivery": delivery_id, "fired": fired, "state": event.state},
        )
        return JSONResponse(
            {"delivery": delivery_id, "fired": fired, "previous_state": event.state},
            status_code=202,
        )

    @app.get("/events")
    async def events(request: Request, limit: int = 50) -> dict[str, Any]:
        rows = request.app.state.bag["db"].list_events(limit=limit)
        return {

View on GitHub (pinned to 9690622007)

Solutions

  1. Check GET /events for the delivery's current state; if it is done/failed, nothing needs cancelling.
  2. For queued deliveries use POST /api/trigger with mode 'retry' (requeues inactive events) rather than cancel.
  3. Refresh the dashboard and retry only if the event is genuinely still running; the state row may have been stale.
  4. Treat the 409 as informational — the task already reached a terminal state; inspect last_error in /events if it failed.
Defensive patterns

Strategy: validation

Validate before calling

const { events } = await (await fetch('/events')).json();
const ev = events.find(e => e.delivery_id === deliveryId);
if (!ev) throw new Error('unknown delivery');
if (ev.state !== 'running') throw new Error(`delivery is ${ev.state}; only running deliveries can be cancelled`);

Try / catch

try {
  await api.cancel(deliveryId);
} catch (e) {
  if (e instanceof ApiError && e.status === 409) {
    console.info('already finished — nothing to cancel; use trigger/retry if needed');
  } else throw e;
}

Prevention

When it happens

Trigger: POST /api/cancel for a delivery whose event.state is 'done' (already finished), 'failed' (already errored or previously cancelled), or 'queued' (not yet claimed by the dispatcher); double-clicking cancel after the first cancel already flipped the row to failed.

Common situations: Operator sees a stale dashboard listing and cancels an already-finished task; two maintainers cancel concurrently; racing the worker which completed the task between fetch and cancel; trying to cancel a queued event that should be retried/cancelled via trigger instead.

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 can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d84bad10f2c64b89. Report an issue: GitHub.