jax-ml/jax · error · ValueError

Unmatched parenthesis in {s!r}

Error message

Unmatched parenthesis in {s!r}

What it means

einshape (used by pallas's jax._src.pallas.einshape) parses each side of an einsum-like reshape equation. When an opening parenthesis has no matching ')' anywhere later in the string, _parse_side raises ValueError 'Unmatched parenthesis'. Grouping parentheses like '(ab)c' declare merged/split dimensions and must be balanced.

Source

Thrown at jax/_src/pallas/einshape.py:144

    "(ab)c" -> [['a', 'b'], ['c']]

  Args:
    s: One side of an einshape equation string.

  Returns:
    A list of lists of characters, where each inner list represents a group of
    dimensions.
  """
  # Remove spaces
  s = s.replace(" ", "")
  groups = []
  i = 0
  while i < len(s):
    if s[i] == "(":
      # Start of a group
      j = s.find(")", i)
      if j == -1:
        raise ValueError(f"Unmatched parenthesis in {s!r}")
      group = list(s[i + 1 : j])
      groups.append(group)
      i = j + 1
    elif s[i] == ")":
      raise ValueError(f"Unmatched parenthesis in {s!r}")
    else:
      # distinct dimension
      groups.append([s[i]])
      i += 1
  return groups


def _parse_equation(equation: str) -> tuple[list[list[str]], list[list[str]]]:
  """Parses an einshape equation."""
  if equation.count("->") != 1:
    raise ValueError("Equation must contain exactly one '->'")
  lhs_str, rhs_str = equation.split("->")
  return _parse_side(lhs_str), _parse_side(rhs_str)

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. Balance the parentheses: every '(' needs a matching ')' on the same side of the equation, e.g. 'a(bc) -> abc'
  2. Programmatically validate with s.count('(') == s.count(')') before calling einshape
  3. Review einshape docs: parentheses denote dimension groups whose sizes multiply; they cannot span the '->'

Example fix

# before
transforms = get_einshape_transforms('a(b -> ab', x.shape)  # unmatched '('

# after
transforms = get_einshape_transforms('a(b) -> ab', x.shape)
Defensive patterns

Strategy: validation

Validate before calling

def eq_side_ok(side: str) -> bool:
    depth = 0
    for ch in side:
        if ch == '(':
            depth += 1
        elif ch == ')':
            depth -= 1
            if depth < 0:
                return False
    return depth == 0

Type guard

null

Try / catch

try:
    t = get_einshape_transforms(eq, shape)
except ValueError as e:
    if 'Unmatched parenthesis' in str(e):
        raise ValueError(f'fix parentheses in einshape equation {eq!r}') from None
    raise

Prevention

When it happens

Trigger: Passing an equation like '...((ab -> ...' or 'a(b' to einshape/reshape helpers — any side string containing '(' with no subsequent ')'. For example jax._src.pallas.einshape.get_einshape_transforms('a(b -> abc', shape).

Common situations: Hand-writing einshape equations with a typo (missing ')'); dynamically building equation strings that drop a closing paren; porting numpy reshape logic to einshape notation incorrectly.

Related errors


AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27). Data as JSON: /api/errors/9589620867adbe33. Report an issue: GitHub.