{"record":{"id":"91203d546a5094a5","repo":"openclaw/openclaw","slug":"fs-sandbox-glob-character-class-cannot-match-path","errorCode":null,"errorMessage":"fs sandbox glob character class cannot match path separators.","messagePattern":"fs sandbox glob character class cannot match path separators\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"extensions/codex/src/app-server/sandbox-exec-server/fs-policy.ts","lineNumber":331,"sourceCode":"  let index = startIndex + 1;\n  if (index >= pattern.length) {\n    throw new Error(\"fs sandbox glob character class must be closed.\");\n  }\n  const negated = pattern[index] === \"!\" || pattern[index] === \"^\";\n  if (negated) {\n    index += 1;\n  }\n  let body = \"\";\n  for (; index < pattern.length; index += 1) {\n    const char = pattern[index];\n    if (char === \"]\" && body) {\n      return {\n        source: `[${negated ? \"^\" : \"\"}${body}]`,\n        endIndex: index,\n      };\n    }\n    if (!char || char === \"/\") {\n      throw new Error(\"fs sandbox glob character class cannot match path separators.\");\n    }\n    body += escapeSandboxGlobCharacterClassChar(char, body.length === 0);\n  }\n  throw new Error(\"fs sandbox glob character class must be closed.\");\n}\n\nfunction escapeSandboxGlobCharacterClassChar(char: string, first: boolean): string {\n  if (char === \"\\\\\" || char === \"]\") {\n    return `\\\\${char}`;\n  }\n  if (first && char === \"^\") {\n    return \"\\\\^\";\n  }\n  return char;\n}\n\nfunction sandboxGlobLiteralPrefix(pattern: string): string {\n  const wildcardIndex = pattern.search(/[*?[]/u);","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/openclaw/openclaw/blob/01804a75319da4b69c9ab98ceaa30477e22b8c0b/extensions/codex/src/app-server/sandbox-exec-server/fs-policy.ts#L313-L349","documentation":"Inside a glob character class '[...]', the compiler rejects '/' and empty (falsy) characters because a class must not match path separators — this keeps the single-segment-wildcard invariant so classes cannot cross directory boundaries. Triggered when char is undefined or '/'.","triggerScenarios":"A glob like '/src/[a/b]' or '/[\\/]bin' where a '/' appears inside the brackets; also when the iteration yields an empty character. The check fires before the character is appended to the class body.","commonSituations":"Attempting to match multiple path segments with a single class; copy-paste of regex into a glob; patterns that try to enumerate separator variants.","solutions":["Remove '/' from inside character classes; use separate glob segments joined by '/' instead","Replace a class that intends to match separators with a '**/' recursive wildcard","Validate that no '[' ... ']' span in the pattern contains a '/' before sending"],"exampleFix":"// before: class contains a separator\n{ pattern: '/src/[a/b]*' }\n\n// after\n{ pattern: '/src/[ab]/*' }","handlingStrategy":"validation","validationCode":"function assertGlobClassNoSeparator(pattern: string): void {\n  let inClass = false;\n  for (const ch of pattern) {\n    if (ch === '[') inClass = true;\n    else if (ch === ']') inClass = false;\n    else if (inClass && ch === '/') throw new Error('glob character class cannot contain /');\n  }\n}","typeGuard":"function globClassHasNoSeparator(pattern: string): boolean {\n  let inClass = false;\n  for (const ch of pattern) {\n    if (ch === '[') inClass = true;\n    else if (ch === ']') inClass = false;\n    else if (inClass && ch === '/') return false;\n  }\n  return true;\n}","tryCatchPattern":"try {\n  resolveFsSandboxPolicy(execServer, record);\n} catch (error) {\n  if (error instanceof Error && error.message.includes('cannot match path separators')) {\n    // split the class across segments or remove the '/'\n  } else throw error;\n}","preventionTips":["Never put '/' inside a glob character class","Use '**/' for cross-segment matching instead of a class"],"tags":["sandbox","codex","glob","validation","security"],"backgroundTag":null,"analyzedSha":"01804a75319da4b69c9ab98ceaa30477e22b8c0b","analyzedAt":"2026-08-12T04:37:58.197Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}