microsoft/playwright · error · Error

Invalid glob pattern ${JSON.stringify(glob)}: unmatched '}'

Error message

Invalid glob pattern ${JSON.stringify(glob)}: unmatched '}'

What it means

Thrown by globToRegexPattern when a `}` is encountered while not inside a brace group (inGroup is false). It indicates an unbalanced closing brace with no matching opening `{`, which the converter cannot turn into a valid regex.

Source

Thrown at packages/isomorphic/urlMatch.ts:74

        } else {
          tokens.push('(.*)');
        }
      } else {
        tokens.push('([^/]*)');
      }
      continue;
    }

    switch (c) {
      case '{':
        if (inGroup)
          throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: nested '{' is not supported`);
        inGroup = true;
        tokens.push('(');
        break;
      case '}':
        if (!inGroup)
          throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '}'`);
        inGroup = false;
        tokens.push(')');
        break;
      case ',':
        if (inGroup) {
          tokens.push('|');
          break;
        }
        tokens.push('\\' + c);
        break;
      default:
        tokens.push(escapedChars.has(c) ? '\\' + c : c);
    }
  }
  if (inGroup)
    throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '{'`);
  tokens.push('$');
  return tokens.join('');

View on GitHub (pinned to c8fc3bf8d3)

Solutions

  1. Add the matching opening `{` or remove the stray `}`.
  2. Escape the brace if it is literal: `\}`.
  3. Reconstruct the glob from known-good parts and log it before registering the route.

Example fix

// before
page.route('**/api}', handler);  // unmatched }

// after
page.route('**/api', handler);  // or '**/\}' for literal brace
Defensive patterns

Strategy: validation

Validate before calling

function assertBracesBalanced(glob: string) {
  let depth = 0;
  for (const c of glob) {
    if (c === '{') depth++;
    else if (c === '}') depth--;
    if (depth < 0) throw new Error(`Glob has unmatched }: ${glob}`);
  }
  if (depth !== 0) throw new Error(`Glob has unmatched {: ${glob}`);
}

Type guard

function hasBalancedBraces(glob: string): boolean {
  let depth = 0;
  for (const c of glob) {
    if (c === '{') depth++;
    else if (c === '}') depth--;
    if (depth < 0) return false;
  }
  return depth === 0;
}

Try / catch

try {
  page.route(glob, handler);
} catch (e) {
  if (/unmatched '\}'/.test(e.message)) {
    glob = glob.replace(/(^|[^{])}/g, '$1\\}'); // escape stray brace
    page.route(glob, handler);
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a glob with a stray `}`, e.g. `'**/api}'`, `page.route('**/foo,bar}', ...)`, or any glob where `}` appears without a preceding `{`. Triggered in the `case '}'` branch when inGroup is false.

Common situations: Typo in a URL glob; truncation that drops the opening brace; JSON/templating that injects a stray closing brace; mis-pasting a pattern fragment.

Related errors


AI-assisted analysis of microsoft/playwright@c8fc3bf8d3 (2026-08-12). Data as JSON: /api/errors/04a5deef4c061f3d. Report an issue: GitHub.