ErrLookupBackground articles › "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

"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

Invalid state transition errors ("status must be '%s', actually '%s'", "MCP server is already rejected.", "Cart is already charging!", "Cannot update task with status: completed", "illegal ... job transition from queued to completed") fire when code triggers an operation from a lifecycle state that does not allow it — a second start(), a retried rejection, a step run out of order, or a write to an already-terminal record. This article explains the state-machine mechanism behind the family, the triggers that recur across libraries, and the checks that prevent them.

Distilled from 116 documented records across 31 repositories.

Background

These errors come from explicit state machines embedded inside libraries. The library enumerates an object's statuses and defines which transitions are legal: NautilusTrader's ComponentState::transition accepts only enumerated (state, trigger) pairs, claude-mem ships an ALLOWED_JOB_TRANSITIONS map, NautilusTrader's execution layer backs a transition table (execution_transition_allowed) with database guards, and Phabricator's Drydock hard-codes status preconditions such as STATUS_PENDING before acquireOnResource(). When a call arrives whose (current state, requested operation) pair is not in the table, the guard throws before side effects happen — NautilusTrader rejects a replacement recording atomically before any writes, and Gumroad's rich-content guards fire inside the transaction after a product lock.

The guards exist to protect invariants the library cannot verify any other way: exactly one in-flight charge per Phortune cart, at most one fill/terminal marker per execution intent, no graph mutations after GitNexus has streamed relationship rows to CSV, terminal task records frozen against reassignment in ruflo. Because the check is an invariant rather than a transient condition, Phabricator explicitly documents that retrying the identical call on the same object fails identically — the state must be reconciled, not re-attempted. Vector's fanout shows the same idea at component scale: a Replace control message for a sink id that was never Added (or was already Removed) panics because the control-message sequence itself is invalid.

From the caller's side the messages cluster into recognizable shapes: "status must be X, actually Y" (Drydock), "already X" (Phortune's "Cart is already charging!", LiteLLM's "MCP server is already rejected.", Paperclip's "Plugin ... is already uninstalled"), "cannot X while/with Y" (React DevTools' "Profiling data cannot be updated while profiling is in progress.", OpenProject's import run "cannot be removed while it is running"), and explicit from-to forms ("illegal observation generation job transition from queued to completed", "Invalid state trigger Running -> Start"). They surface as exceptions, HTTP 400 responses, or — for Vector — a panic that indicates an internal bug to report upstream.

Where libraries disagree is idempotency and severity, and that behavior is library-specific: NautilusTrader tolerates same-status repeats as idempotent but throws on genuinely out-of-order transitions; GitNexus's plain KnowledgeGraph.removeRelationship returns false for unknown ids while the streaming sink throws on any id; Paperclip's docs suggest treating a 400 "already uninstalled" as success in automation, while NautilusTrader's marker guard is documented as a signal of dispatcher logic bugs, not a transient fault. Deciding which variant you face — retryable after state advances, idempotent no-op, or logic bug — is the first step of every fix in this family.

Common causes

What usually fixes it

Go deeper

Documented occurrences

…and 96 more across the corpus — use search.

Honest provenance: generated on 2026-08-21 from AI-assisted analysis of the linked records. See how records are made.