{"record":{"id":"a2054b9a9fbf5f35","repo":"microsoft/playwright","slug":"invalid-glob-pattern-json-stringify-glob-unma-a2054b","errorCode":null,"errorMessage":"Invalid glob pattern ${JSON.stringify(glob)}: unmatched '{'","messagePattern":"Invalid glob pattern (.+?): unmatched '\\{'","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/isomorphic/urlMatch.ts","lineNumber":90,"sourceCode":"      case '}':\n        if (!inGroup)\n          throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '}'`);\n        inGroup = false;\n        tokens.push(')');\n        break;\n      case ',':\n        if (inGroup) {\n          tokens.push('|');\n          break;\n        }\n        tokens.push('\\\\' + c);\n        break;\n      default:\n        tokens.push(escapedChars.has(c) ? '\\\\' + c : c);\n    }\n  }\n  if (inGroup)\n    throw new Error(`Invalid glob pattern ${JSON.stringify(glob)}: unmatched '{'`);\n  tokens.push('$');\n  return tokens.join('');\n}\n\nfunction isRegExp(obj: any): obj is RegExp {\n  return obj instanceof RegExp || Object.prototype.toString.call(obj) === '[object RegExp]';\n}\n\nexport type URLMatch = string | RegExp | ((url: URL) => boolean) | URLPattern;\n// URLPattern is not in @types/node@18, so we polyfill it ourselves\nexport type URLPattern = {\n  test(input: string | URL): boolean;\n  hash: string;\n  hostname: string;\n  password: string;\n  pathname: string;\n  port: string;\n  protocol: string;","sourceCodeStart":72,"sourceCodeEnd":108,"githubUrl":"https://github.com/microsoft/playwright/blob/c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6/packages/isomorphic/urlMatch.ts#L72-L108","documentation":"Thrown by globToRegexPattern after processing all characters when inGroup is still true, i.e. a `{` was opened but never closed with `}`. The incomplete alternation cannot produce a valid regex, so the converter rejects it.","triggerScenarios":"Passing a glob with an unterminated brace group, e.g. `'{foo,bar'`, `page.route('**/{api', ...)`, or any glob where `{` has no matching `}`. Triggered by the `if (inGroup)` check after the loop.","commonSituations":"Truncated glob string; template interpolation that omits the closing brace; partial paste of an alternation pattern; off-by-one in dynamic glob construction.","solutions":["Add the missing closing `}` to complete the alternation.","Escape the `{` as `\\{` if it is meant literally.","Validate brace balance in dynamic glob strings before use."],"exampleFix":"// before\nconst env = 'staging';\npage.route(`**/{${env}`, handler);  // missing }\n\n// after\npage.route(`**/{${env}}`, handler);","handlingStrategy":"validation","validationCode":"function assertGlobTerminated(glob: string) {\n  const opens = (glob.match(/{/g) ?? []).length;\n  const closes = (glob.match(/}/g) ?? []).length;\n  if (opens !== closes) throw new Error(`Glob braces unbalanced (${opens}/{ vs ${closes}/}): ${glob}`);\n}","typeGuard":"function isTerminatedGlob(glob: string): boolean {\n  return (glob.match(/{/g) ?? []).length === (glob.match(/}/g) ?? []).length;\n}","tryCatchPattern":"try {\n  page.route(glob, handler);\n} catch (e) {\n  if (/unmatched '\\{'/.test(e.message)) {\n    glob += '}'; // append missing closer when safe to infer\n    page.route(glob, handler);\n  } else throw e;\n}","preventionTips":["When interpolating a list into `{...}`, ensure the closing brace is always emitted.","Validate brace balance in glob-building helpers.","Prefer a RegExp over complex globs."],"tags":["glob","url-matching","parsing","validation"],"backgroundTag":null,"analyzedSha":"c8fc3bf8d31542d59b4d4d9eaab1df93ff541dc6","analyzedAt":"2026-08-12T07:26:36.950Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}