sgl-project/sglang · error · ValueError

Pi05 v1 expects one state vector per request

Error message

Pi05 v1 expects one state vector per request

What it means

The Pi05 v1 stage requires exactly one robot state vector per action request. After converting the state to a float32 tensor (and unsqueezing 1-D inputs to shape [1, D]), it rejects any tensor whose batch dimension is not exactly 1, matching the single-request design of the v1 API.

Source

Thrown at python/sglang/multimodal_gen/runtime/pipelines_core/stages/model_specific_stages/pi05_preprocess.py:185

            is_present = value is not None and bool(image_masks_in.get(key, True))
            if is_present:
                tensor = _preprocess_image(value, self.config.image_size)
            else:
                channels = 3
                height, width = self.config.image_size
                tensor = torch.ones(channels, height, width, dtype=torch.float32) * -1.0

            images[key] = tensor.unsqueeze(0)
            image_masks[key] = torch.tensor([is_present], dtype=torch.bool)

        state = raw_observation.get("state")
        state_tensor = None
        if state is not None:
            state_tensor = torch.as_tensor(state, dtype=torch.float32)
            if state_tensor.ndim == 1:
                state_tensor = state_tensor.unsqueeze(0)
            if state_tensor.shape[0] != 1:
                raise ValueError("Pi05 v1 expects one state vector per request")
            if state_tensor.shape[-1] > self.config.state_dim:
                raise ValueError(
                    f"Pi05 state dim must be <= {self.config.state_dim}, "
                    f"got {state_tensor.shape[-1]}"
                )

        noise = raw_observation.get("noise")
        noise_tensor = None
        if noise is not None:
            noise_tensor = torch.as_tensor(noise, dtype=torch.float32)
            if noise_tensor.ndim == 2:
                noise_tensor = noise_tensor.unsqueeze(0)
            expected = (1, self.config.action_horizon, self.config.action_dim)
            if tuple(noise_tensor.shape) != expected:
                raise ValueError(
                    f"Pi05 noise must have shape {expected}, "
                    f"got {tuple(noise_tensor.shape)}"
                )

View on GitHub (pinned to 0132848349)

Solutions

  1. Slice your state array per timestep: state = states[t:t+1] (or states[t] which gets unsqueezed automatically).
  2. Verify state_tensor.ndim is 1 or 2 with shape[0]==1 before calling the stage.
  3. Batch by looping over requests rather than stacking states along dim 0.

Example fix

# before
state = episode_states  # shape [T, D]
stage({"state": state, ...})

# after
for t in range(episode_states.shape[0]):
    stage({"state": episode_states[t], ...})
Defensive patterns

Strategy: validation

Validate before calling

import torch
s = torch.as_tensor(state, dtype=torch.float32)
if s.ndim == 1:
    s = s.unsqueeze(0)
assert s.shape[0] == 1, f"expected 1 state, got {s.shape[0]}"

Type guard

def is_single_state(state) -> bool:
    if state is None: return True
    s = torch.as_tensor(state)
    return s.ndim <= 2 and (s.ndim < 2 or s.shape[0] == 1)

Prevention

When it happens

Trigger: Passing raw_observation state with shape [N, D] where N > 1 (a batched state matrix), or a nested list like [[s1...],[s2...]] containing multiple state vectors.

Common situations: Replaying recorded robot episodes where states are stored as [T, D] time-series arrays and the whole trajectory is passed at once instead of per-timestep; migrating from an ensemble/batched policy.

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/d659fe9891e2ae68. Report an issue: GitHub.