microsoft/qlib · error · GymSpaceValidationError
Validation error reported by gym.
Error message
Validation error reported by gym.
What it means
Leaf-level failure in qlib's space validator (qlib/rl/interpreter.py:131). For any space that is not Dict or Tuple (Box, Discrete, MultiDiscrete, ...), the standard `gym.Space.contains(x)` is used; if gym itself rejects the sample, qlib re-raises it as a GymSpaceValidationError carrying the space and sample for diagnostics.
Source
Thrown at qlib/rl/interpreter.py:131
try:
_gym_space_contains(subspace, x[k])
except GymSpaceValidationError as e:
raise GymSpaceValidationError(f"Subspace of key {k} validation error.", space, x) from e
elif isinstance(space, spaces.Tuple):
if isinstance(x, (list, np.ndarray)):
x = tuple(x) # Promote list and ndarray to tuple for contains check
if not isinstance(x, tuple) or len(x) != len(space):
raise GymSpaceValidationError("Sample must be a tuple with same length as space.", space, x)
for i, (subspace, part) in enumerate(zip(space, x)):
try:
_gym_space_contains(subspace, part)
except GymSpaceValidationError as e:
raise GymSpaceValidationError(f"Subspace of index {i} validation error.", space, x) from e
else:
if not space.contains(x):
raise GymSpaceValidationError("Validation error reported by gym.", space, x)
class GymSpaceValidationError(Exception):
def __init__(self, message: str, space: gym.Space, x: Any) -> None:
self.message = message
self.space = space
self.x = x
def __str__(self) -> str:
return f"{self.message}\n Space: {self.space}\n Sample: {self.x}"
View on GitHub (pinned to 79633dd950)
Solutions
- Use the exception's `space` and `x` attributes to see exactly which value violated which bounds/shape.
- Fix the data pipeline: normalize features, forward/backward-fill or drop NaN rows before they reach the interpreter.
- Match dtype and shape exactly when constructing samples (e.g. `np.asarray(x, dtype=np.float32).reshape(space.shape)`).
Example fix
// before obs = raw_price_series # values ~ 1e2..1e4, Box(-1, 1) // after obs = (raw_price_series - mean) / std # normalized into Box bounds
Defensive patterns
Strategy: validation
Validate before calling
import numpy as np
def check_leaf(space, x):
x = np.asarray(x)
assert space.contains(x), f"{x!r} (shape={x.shape}, dtype={x.dtype}) not in {space}" Type guard
def in_leaf_space(space: gym.Space, x: Any) -> bool:
import numpy as np
try:
return space.contains(np.asarray(x, dtype=getattr(space, 'dtype', None)))
except Exception:
return False Try / catch
try:
_gym_space_contains(space, x)
except GymSpaceValidationError as e:
if e.message.startswith("Validation error reported by gym"):
log.error("leaf %r violates space %s", e.x, e.space)
raise Prevention
- Normalize all observation features; never feed raw prices into bounded Boxes.
- Drop or impute NaN rows before rollout.
- Pin gym/numpy versions and re-run space smoke tests after upgrades.
When it happens
Trigger: Validating a Box/Discrete leaf space with an out-of-bounds value, wrong shape, wrong dtype class, or NaN where not allowed. E.g. `spaces.Discrete(3)` with sample `3`, or `spaces.Box(0, 1, shape=(2,))` with sample `np.array([0.5])`.
Common situations: Unnormalized observations (prices in thousands against Box(-1,1)); integer vs float dtype confusion under newer gym/numpy versions where `contains` got stricter; NaN leaking from missing market data into a non-NaN-tolerant Box.
Related errors
- Key {k} not found in sample.
- Subspace of key {k} validation error.
- Sample must be a tuple with same length as space.
- Subspace of index {i} validation error.
- Sample must be a dict with same length as space.
AI-assisted analysis of microsoft/qlib@79633dd950 (2026-08-15).
Data as JSON: /api/errors/85b165aec748bc5c.
Report an issue: GitHub.