sgl-project/sglang · warning · ValueError
Cannot transition {request_id} from terminal state {old_stat
Error message
Cannot transition {request_id} from terminal state {old_state.value} to {new_state.value} What it means
Raised by RequestStateStore.transition when moving a request that is already in a terminal state (FAILED/TIMED_OUT, or any non-active state) into another terminal state. Terminal states are final; the state machine only allows FAILED/TIMED_OUT transitions from active states, so re-failing or transitioning a finished request is rejected.
Source
Thrown at python/sglang/multimodal_gen/runtime/disaggregation/request_state.py:111
request_id: str,
new_state: RequestState,
*,
error: str | None = None,
encoder_instance: int | None = None,
denoiser_instance: int | None = None,
decoder_instance: int | None = None,
) -> RequestRecord:
with self._lock:
record = self._requests.get(request_id)
if record is None:
raise ValueError(f"Unknown request_id: {request_id}")
old_state = record.state
if new_state in _TERMINAL_STATES and new_state != RequestState.DONE:
# FAILED / TIMED_OUT: allowed from any active state
if old_state not in _ACTIVE_STATES:
raise ValueError(
f"Cannot transition {request_id} from terminal state "
f"{old_state.value} to {new_state.value}"
)
elif new_state not in _VALID_TRANSITIONS.get(old_state, set()):
raise ValueError(
f"Invalid transition for {request_id}: "
f"{old_state.value} -> {new_state.value}"
)
record.state = new_state
record.last_transition_time = time.monotonic()
if error is not None:
record.error = error
if encoder_instance is not None:
record.encoder_instance = encoder_instance
if denoiser_instance is not None:
record.denoiser_instance = denoiser_instance
if decoder_instance is not None:View on GitHub (pinned to 0132848349)
Solutions
- Make error completion idempotent: check record.state is active (or catch this ValueError) before applying FAILED/TIMED_OUT
- Serialize completions per request (e.g. via the store lock or a per-request completion flag) so only the first terminal transition wins
- Cancel the watchdog/timeout task once a request reaches a terminal state
- Log and drop duplicate terminal transitions at debug level instead of letting them propagate
Example fix
# before
store.transition(request_id, RequestState.FAILED) # may race -> ValueError
# after
record = store.get(request_id)
if record is None or record.state in _TERMINAL_STATES:
return # already finished; idempotent no-op
try:
store.transition(request_id, RequestState.FAILED)
except ValueError:
logger.debug("request %s already terminal", request_id) Defensive patterns
Strategy: try-catch
Validate before calling
from sglang.multimodal_gen.runtime.disaggregation.request_state import _TERMINAL_STATES, _ACTIVE_STATES
record = store.get(request_id)
if record is not None and record.state not in _ACTIVE_STATES:
logger.debug("%s already terminal (%s); skipping error completion", request_id, record.state)
return Try / catch
try:
store.transition(request_id, RequestState.FAILED)
except ValueError as e:
if "terminal state" in str(e):
logger.debug("duplicate terminal transition for %s ignored", request_id)
return
raise Prevention
- Make error completion idempotent — only the first terminal transition should win
- Cancel watchdog/timeout timers once a request reaches a terminal state
- Check record.state before calling transition with FAILED/TIMED_OUT
When it happens
Trigger: Calling transition(id, FAILED or TIMED_OUT) when the record's current state is already terminal — e.g. two components race to report failure (timeout watchdog fires after _complete_with_error already marked the request FAILED), or a duplicate error frame is processed.
Common situations: Concurrent error paths (watchdog timeout + decoder error), duplicate error frames from retries, or handlers not checking the record state before completing with an error.
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
- Invalid transition for {request_id}: {old_state.value} -> {n
- Duplicate request_id: {request_id}
- Unknown request_id: {request_id}
- No embedding available for request: {state.req_id}
- Missing previous frame for delta payload
AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28).
Data as JSON: /api/errors/194e256cf52c38e9.
Report an issue: GitHub.