{"record":{"id":"90d053486160248e","repo":"neoclide/coc.nvim","slug":"invalid-regex-e-instanceof-error-e-message","errorCode":null,"errorMessage":"Invalid regex: ${e instanceof Error ? e.message : String(e)}","messagePattern":"Invalid regex: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/mcp/tools/workspace.ts","lineNumber":130,"sourceCode":"    || /\\(\\?[=!<]/.test(pattern)\n    || /\\([^)]*[+*{][^)]*\\)\\s*(?:[+*?]|\\{)/.test(pattern)\n}\n\nexport async function searchWithJs(pattern: string, args: any, root: string, maxResults: number): Promise<SearchMatch[]> {\n  if (args.regex === true && unsafeFallbackRegex(pattern)) {\n    throw new Error('Regex is too complex for the JavaScript search fallback; install ripgrep to use it safely')\n  }\n  let include = new RelativePatternImpl(URI.file(root), typeof args.include === 'string' && args.include ? args.include : '**/*')\n  let uris = await workspace.findFiles(include, args.exclude || null, 500)\n  // One (first) match per line, searched from the start of every line: the\n  // global flag would carry lastIndex across lines and skip matches.\n  let flags = args.caseSensitive === true ? '' : 'i'\n  let source = args.regex === true ? pattern : escapeRegExp(pattern)\n  let re: RegExp\n  try {\n    re = new RegExp(source, flags)\n  } catch (e) {\n    throw new Error(`Invalid regex: ${e instanceof Error ? e.message : String(e)}`)\n  }\n  let results: SearchMatch[] = []\n  for (let uri of uris) {\n    if (results.length >= maxResults) break\n    let filepath = uri.fsPath\n    if (checkPath(filepath)) continue\n    let content: string\n    try {\n      let stat = fs.statSync(filepath)\n      if (stat.size > 2 * 1024 * 1024) continue\n      content = fs.readFileSync(filepath, 'utf8')\n    } catch (_e) {\n      continue\n    }\n    if (content.includes('\\0')) continue\n    let lines = content.split('\\n')\n    for (let i = 0; i < lines.length; i++) {\n      if (results.length >= maxResults) break","sourceCodeStart":112,"sourceCodeEnd":148,"githubUrl":"https://github.com/neoclide/coc.nvim/blob/50e974d9692461a69147d5cab146a8d3e439abe4/src/mcp/tools/workspace.ts#L112-L148","documentation":"searchWithJs in src/mcp/tools/workspace.ts builds a RegExp from user-supplied search input. If args.regex is true the pattern is used verbatim; otherwise it is escaped. When the pattern (verbatim mode) is not a valid regular expression, `new RegExp` throws and the library rethrows it wrapped as 'Invalid regex: <underlying message>'. This guards the MCP workspace search tool from crashing on malformed patterns.","triggerScenarios":"Calling the workspace search tool with args.regex=true and a syntactically invalid pattern, e.g. '[abc', 'a{2,1}', a dangling backslash, or an invalid group like '(?P<name>x)'.","commonSituations":"LLM/MCP clients generating regex patterns dynamically with unbalanced brackets; users pasting globs or wildcard patterns (e.g. 'src/**/*.ts') into a regex-mode search; patterns containing unmatched '(' from copy-paste.","solutions":["Set args.regex=false (or omit it) so the pattern is escaped literally via escapeRegExp and always valid.","Test the pattern in a JS runtime (new RegExp(pattern)) before sending it with regex=true.","Fix the syntax error reported in the wrapped message (unmatched bracket, invalid quantifier, dangling escape)."],"exampleFix":"// before\nawait search({ pattern: 'src/(foo', regex: true })\n// after\nawait search({ pattern: 'src/(foo', regex: false }) // or fix regex: 'src\\/(foo","handlingStrategy":"validation","validationCode":"function isValidRegex(p) { try { new RegExp(p); return true } catch { return false } }\nif (args.regex === true && !isValidRegex(pattern)) throw new Error(`Invalid regex: ${pattern}`)","typeGuard":"function isSafePattern(p: unknown): p is string { return typeof p === 'string' && (() => { try { new RegExp(p); return true } catch { return false } })() }","tryCatchPattern":"try {\n  await workspaceSearch({ pattern, regex: true })\n} catch (e) {\n  if (String(e.message).startsWith('Invalid regex:')) return searchLiteral(pattern)\n  throw e\n}","preventionTips":["Only set regex=true for patterns you have validated with new RegExp in the client.","Default to regex=false (literal/escaped) search unless regex semantics are required.","Test patterns in regex101 or a REPL before sending them from tooling."],"tags":[],"backgroundTag":null,"analyzedSha":"50e974d9692461a69147d5cab146a8d3e439abe4","analyzedAt":"2026-08-31T11:17:23.966Z","schemaVersion":2},"datasetVersion":"2026-09-01T08:17:40.651Z"}