jax-ml/jax · error · ValueError
The symbolic constraints should be a sequence of strings. Go
Error message
The symbolic constraints should be a sequence of strings. Got {repr(constraints_str)} What it means
SymbolicScope (created implicitly by polymorphic_shapes with constraints) expects a sequence of constraint strings like ('n >= 4',). Passing a single string would be iterated character-by-character, so JAX rejects it explicitly with this ValueError.
Source
Thrown at jax/_src/export/shape_poly.py:1002
All symbolic expressions that interact (e.g., appear in the argument shapes
for one JAX function invocation, or are involved in arithmetic operations)
must be from the same scope and must share the same SymbolicScope object.
Holds the constraints on symbolic expressions.
See [the README](https://docs.jax.dev/en/latest/export/shape_poly.html#user-specified-symbolic-constraints)
for more details.
Args:
constraints_str: A sequence of constraints on symbolic dimension expressions,
of the form `e1 >= e2` or `e1 <= e2` or `e1 == e2`.
"""
def __init__(self,
constraints_str: Sequence[str] = ()):
if isinstance(constraints_str, str):
raise ValueError(
"The symbolic constraints should be a sequence of strings. "
f"Got {repr(constraints_str)}")
self._initialized = False
self._location_frame = source_info_util.user_frame(
source_info_util.current().traceback)
# Keep the explicit constraints in the order in which they were added
self._explicit_constraints: list[_SymbolicConstraint] = []
# We cache the _DimExpr.bounds calls. The result depends only on the
# explicit and implicit constraints, so it is safe to keep it in the
# scope. Set the cache before we parse constraints. We also keep the
# bounds precision with which we computed the cached result.
self._bounds_cache: dict[_DimExpr,
tuple[float, float, BoundsPrecision]] = {}
# We store here a decision procedure state initialized with all the
# _explicit_constraints.
self._decision_initial_state: Any | None = NoneView on GitHub (pinned to 1e1c6a8fc0)
Solutions
- Wrap the constraint(s) in a list or tuple: constraints=('n >= 4',) or ['a == b*2']
- Check for accidental string concatenation producing a single str
- Verify the parameter type at the call site before passing
Example fix
# before
scope = jax.export.shape_poly.SymbolicScope('n >= 4')
# after
scope = jax.export.shape_poly.SymbolicScope(('n >= 4',)) Defensive patterns
Strategy: validation
Validate before calling
assert not isinstance(constraints, str), 'pass a sequence of strings, e.g. ("n >= 4",)' Type guard
def valid_constraints(c) -> bool:
return not isinstance(c, str) and all(isinstance(s, str) for s in c) Try / catch
try:
scope = SymbolicScope(constraints)
except ValueError as e:
if 'sequence of strings' in str(e): scope = SymbolicScope((constraints,)) Prevention
- Always wrap constraint strings in a tuple/list literal with trailing comma
- Lint call sites that accept sequences for accidental bare strings
When it happens
Trigger: Passing constraints='n>=4' (a bare string) instead of a tuple/list ['n>=4'] anywhere constraints are accepted (SymbolicScope(...), shapes with constraints, jax.export APIs).
Common situations: Copy-paste from docs where a tuple lost its parentheses; refactoring that turned a list into an optional string.
Related errors
- SymbolicScope constraint must be a string: got {repr(c_str)}
- Constraint parsing error: must contain one of '==' or '>=' o
- Unsatisfiable explicit constraint: {constr.debug_str}
- Invalid equality constraint: {e1} == {e2}. The left-hand-sid
- Found multiple equality constraints with the same left-hand-
AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27).
Data as JSON: /api/errors/6029af5c0762ca77.
Report an issue: GitHub.