pandas-dev/pandas · error · ValueError

{pat=} must contain a symbolic group name.

Error message

{pat=} must contain a symbolic group name.

What it means

Raised by ArrowExtensionArray._str_extract when the compiled regex has no symbolic (named) groups. The ArrowExtensionArray implementation builds result columns keyed by `re.compile(pat).groupindex.keys()`, so at least one named capture group `(?P<name>...)` is mandatory. An unnamed group or a groupless pattern is rejected with ValueError before any extraction runs.

Source

Thrown at pandas/core/arrays/arrow/array.py:3669

        result = self._apply_elementwise(predicate)
        return self._from_pyarrow_array(pa.chunked_array(result))

    def _str_casefold(self) -> Self:
        predicate = lambda val: val.casefold()
        result = self._apply_elementwise(predicate)
        return self._from_pyarrow_array(pa.chunked_array(result))

    def _str_encode(self, encoding: str, errors: str = "strict") -> Self:
        predicate = lambda val: val.encode(encoding, errors)
        result = self._apply_elementwise(predicate)
        return self._from_pyarrow_array(pa.chunked_array(result))

    def _str_extract(self, pat: str, flags: int = 0, expand: bool = True):
        if flags:
            raise NotImplementedError("Only flags=0 is implemented.")
        groups = re.compile(pat).groupindex.keys()
        if len(groups) == 0:
            raise ValueError(f"{pat=} must contain a symbolic group name.")
        result = pc.extract_regex(self._pa_array, pat)
        if expand:
            return {
                col: self._from_pyarrow_array(pc.struct_field(result, [i]))
                for col, i in zip(groups, range(result.type.num_fields), strict=True)
            }
        else:
            return type(self)(pc.struct_field(result, [0]))

    def _str_findall(self, pat: str, flags: int = 0) -> Self:
        regex = re.compile(pat, flags=flags)
        predicate = lambda val: regex.findall(val)
        result = self._apply_elementwise(predicate)
        return self._from_pyarrow_array(pa.chunked_array(result))

    def _str_get_dummies(self, sep: str = "|", dtype: NpDtype | None = None):
        if dtype is None:
            dtype = np.bool_

View on GitHub (pinned to 71959b8cb9)

Solutions

  1. Add a symbolic name to the group: `s.str.extract(r"(?P<num>\\d+)")`.
  2. If you only need a boolean/match, use `s.str.contains(pat)` or `s.str.match(pat)` instead of str.extract.
  3. If positional columns are required, cast to string[python]/object: `s.astype("string[python]").str.extract(r"(\\d+)")`.
  4. Name every group when you have multiple captures so each column is addressable.

Example fix

# before
s = pd.Series(["a1","b2"], dtype="string[pyarrow]")
s.str.extract(r"(\\d+)")  # ValueError

# after
s.str.extract(r"(?P<digit>\\d+)")
Defensive patterns

Strategy: validation

Validate before calling

import re

def has_named_group(pat: str) -> bool:
    return len(re.compile(pat).groupindex) > 0

def safe_extract(s, pat):
    if not has_named_group(pat):
        raise ValueError(f"pattern {pat!r} needs at least one named group (?P<name>...)")
    return s.str.extract(pat)

Type guard

import re

def is_named_group_pattern(pat: str) -> bool:
    try:
        return len(re.compile(pat).groupindex) > 0
    except re.error:
        return False

Try / catch

try:
    out = s.str.extract(pat)
except ValueError as e:
    if "symbolic group name" in str(e):
        # add a default name and retry
        out = s.str.extract(f"(?P<g0>{pat})")
    else:
        raise

Prevention

When it happens

Trigger: Calling `s.str.extract(r"(\\d+)")` (unnamed group) or `s.str.extract(r"\\d+")` (no group at all) on a pyarrow-backed string Series. With expand=True (the default) on this backend, every capture must be named.

Common situations: Patterns authored for object/string[python] dtypes where unnamed groups returned numeric columns (0, 1, ...). Copy-pasting regex from another language/tool that uses positional groups. Using str.extract for boolean matching instead of str.contains.

Related errors


AI-assisted analysis of pandas-dev/pandas@71959b8cb9 (2026-08-07). Data as JSON: /api/errors/39a606025cc08c5c. Report an issue: GitHub.