Unity-Technologies/ml-agents · error · UnityObservationException

Observation did not have the expected shape - got {obs.shape

Error message

Observation did not have the expected shape - got {obs.shape} but expected {expected_shape}

What it means

UnityObservationException raised by _observation_to_np_array when an observation proto's declared shape (obs.shape) doesn't equal the expected_shape from the ObservationSpec. The library validates before converting to numpy to give a clearer error than raw reshape failures.

Source

Thrown at ml-agents-envs/mlagents_envs/rpc_utils.py:227

                f"Observation at index={obs_index} for agent with "
                f"id={agent_info.id} didn't match the ObservationSpec. "
                f"Expected shape {expected_obs_shape} but got {agent_obs_shape}."
            )


@timed
def _observation_to_np_array(
    obs: ObservationProto, expected_shape: Optional[Iterable[int]] = None
) -> np.ndarray:
    """
    Converts observation proto into numpy array of the appropriate size.
    :param obs: observation proto to be converted
    :param expected_shape: optional shape information, used for sanity checks.
    :return: processed numpy array of observation from environment
    """
    if expected_shape is not None:
        if list(obs.shape) != list(expected_shape):
            raise UnityObservationException(
                f"Observation did not have the expected shape - got {obs.shape} but expected {expected_shape}"
            )
    expected_channels = obs.shape[0]
    if obs.compression_type == COMPRESSION_TYPE_NONE:
        img = np.array(obs.float_data.data, dtype=np.float32)
        img = np.reshape(img, obs.shape)
        return img
    else:
        img = process_pixels(
            obs.compressed_data, expected_channels, list(obs.compressed_channel_mapping)
        )
        # Compare decompressed image size to observation shape and make sure they match
        if list(obs.shape) != list(img.shape):
            raise UnityObservationException(
                f"Decompressed observation did not have the expected shape - "
                f"decompressed had {img.shape} but expected {obs.shape}"
            )
        return img

View on GitHub (pinned to 3ecb446f75)

Solutions

  1. Close and relaunch UnityEnvironment to refresh the BehaviorSpec.
  2. Ensure every agent with the same behavior name has identical observation sizes (camera resolution, stacking, vec obs size).
  3. Align com.unity.ml-agents and mlagents-envs package versions and rebuild.
  4. Reset the environment after any editor-side changes (env.reset()) before stepping.

Example fix

// before
# editor changed Vector Observation Space Size from 8 to 12 mid-session
env.step()  # got [12] but expected [8]
// after
env.close(); env = UnityEnvironment(...)  # spec refreshed to [12]
Defensive patterns

Strategy: try-catch

Validate before calling

from mlagents_envs_envs import nothing  # check specs before stepping

def obs_specs_match(env, behavior, expected_shapes):
    specs = env.behavior_specs[behavior].observation_specs
    return [tuple(s.shape) for s in specs] == expected_shapes

Try / catch

from mlagents_envs.exception import UnityObservationException

try:
    env.step()
except UnityObservationException as e:
    if "did not have the expected shape" in str(e):
        env.close()
        env = UnityEnvironment(file_name=env_path)
        env.reset()

Prevention

When it happens

Trigger: _process_maybe_compressed_observation converting an ObservationProto whose obs.shape differs from the expected_shape passed from the BehaviorSpec — spec/data disagreement from Unity.

Common situations: Behavior parameters (vector observation size, camera resolution) changed in Unity after the Python side cached the spec; mismatched ml-agents versions; multiple agents in one scene configured differently despite sharing a behavior name.

Related errors


AI-assisted analysis of Unity-Technologies/ml-agents@3ecb446f75 (2026-09-02). Data as JSON: /api/errors/799e980127c8b876. Report an issue: GitHub.