langchain-ai/deepagents · error · InvalidGlobPatternError

Invalid glob pattern {pattern!r}: {exc}

Error message

Invalid glob pattern {pattern!r}: {exc}

What it means

When the `wcmatch` library refuses to compile a glob pattern, `compile_grep_include_glob` logs a warning (because wcmatch's breadth can also reject valid patterns) and re-raises as `InvalidGlobPatternError` with the underlying exception text chained. This catches truly malformed patterns, non-str inputs, and wcmatch version behavioral changes.

Source

Thrown at libs/deepagents/deepagents/backends/utils.py:165

    flags = wcglob.BRACE | wcglob.GLOBSTAR
    # A leading `/` anchors to the search root: strip it so it matches against
    # the (slash-less) relative path, but decide anchoring from the original
    # pattern so `/*.py` stays root-anchored instead of collapsing to a
    # basename-at-any-depth match.
    anchored = "/" in pattern
    try:
        compiled = wcglob.compile(pattern.lstrip("/"), flags=flags)
    except Exception as exc:
        # `wcmatch` only raises private types (`wcmatch._wcparse.PatternLimitException`),
        # so catch broadly and re-raise a public type: every backend can then catch
        # one public type instead of importing from a private module. Log first --
        # the breadth also swallows genuine bugs (a non-`str` pattern, a wcmatch
        # version bump), which would otherwise reach the user as "invalid pattern"
        # for a pattern that is perfectly valid.
        logger.warning("wcmatch refused glob pattern %r (%s): %s", pattern, type(exc).__name__, exc)
        msg = f"Invalid glob pattern {pattern!r}: {exc}"
        raise InvalidGlobPatternError(msg) from exc

    if anchored:

        def matcher(rel_path: str) -> bool:
            return bool(compiled.match(rel_path))
    else:

        def matcher(rel_path: str) -> bool:
            return bool(compiled.match(PurePosixPath(rel_path).name))

    return matcher


def _normalize_content(file_data: FileData) -> str:
    """Normalize current and legacy file data content to a plain string.

    Args:
        file_data: `FileData` dict with `content` key.

View on GitHub (pinned to a1af029e6e)

Solutions

  1. Fix the glob syntax (balance `[...]` and `{...}`, escape literal `[`/`]`)
  2. Ensure the pattern is a plain str before passing it
  3. Test the pattern against wcmatch directly to see the precise refusal reason in the chained exception
  4. Pin/upgrade the wcmatch version if a previously valid pattern regressed

Example fix

// before
backend.glob('src/[a-*.py')  # unmatched bracket
// after
backend.glob('src/[a-z]*.py')
Defensive patterns

Strategy: try-catch

Validate before calling

def check_glob(pattern):
    if not isinstance(pattern, str):
        raise TypeError('glob pattern must be a string')
    for open_c, close_c in (('[', ']'), ('{', '}')):
        if pattern.count(open_c) != pattern.count(close_c):
            raise ValueError(f'unbalanced {open_c}{close_c} in glob: {pattern!r}')
    return pattern

Type guard

def is_valid_glob_shape(pattern: object) -> bool:
    if not isinstance(pattern, str) or not pattern:
        return False
    return (
        pattern.count('[') == pattern.count(']')
        and pattern.count('{') == pattern.count('}')
    )

Try / catch

from deepagents.backends.utils import InvalidGlobPatternError
import logging
try:
    files = backend.glob(pattern)
except InvalidGlobPatternError as exc:
    logging.warning('bad glob %r: %s', pattern, exc)
    files = []

Prevention

When it happens

Trigger: Passing syntactically invalid globs (e.g. unmatched brackets `[abc`, bad brace nesting `{a,b`) to `glob`/`grep` include or exclude parameters; passing None or a non-string where a pattern string is expected; upgrading wcmatch so a previously accepted pattern is refused.

Common situations: LLM- or user-supplied glob strings; hand-written patterns with unbalanced wildcards; dependency version bumps changing glob grammar strictness.

Related errors


AI-assisted analysis of langchain-ai/deepagents@a1af029e6e (2026-08-29). Data as JSON: /api/errors/94393518564ec748. Report an issue: GitHub.