denoland/deno · error · Error

Invalid RegExp pattern

Error message

Invalid RegExp pattern: ${raw}

What it means

Attribute values in lint selectors may be regex literals written /pattern/flags (e.g. [id.name=/^_foo$/]). getFromRawValue in cli/js/40_lint_selector.js splits the raw token on the last '/' to separate pattern from flags and throws `Invalid RegExp pattern: <raw>` when it cannot find a closing slash. Note that as written the -1 check cannot fire while the value starts with '/', so in practice an unterminated regex like [id.name=/_foo] more often surfaces as a RegExp constructor SyntaxError (garbage parsed as flags) — same root cause: the regex literal is not properly delimited.

Solutions

  1. Close the regex literal: [id.name=/_foo] → [id.name=/_foo/] (optionally anchored: /^_foo$/).
  2. If you meant a literal string match, use quoting instead: [id.name="_foo"].
  3. If you see a RegExp SyntaxError about flags on the same key, check for the same missing-slash problem.

Example fix

// before
"Declarator[id.name=/_temp]"(node) { /* ... */ },

// after
"Declarator[id.name=/^_temp$/]"(node) { /* ... */ },
Defensive patterns

Strategy: validation

Validate before calling

function checkRegexAttrs(key) {
  for (const m of key.matchAll(/\[([^=\]]+)=([^\]]*)\]/g)) {
    const raw = m[2];
    if (raw.startsWith("/") && !/\/.+\/[a-z]*$/.test(raw)) {
      throw new Error(`Unterminated regex value in "${key}": ${raw}`);
    }
  }
}

Prevention

When it happens

Trigger: A visitor key with a regex attribute value missing the closing slash: "Declarator[id.name=/_temp]", or a value that begins with '/' but is never closed, so the parser cannot split pattern and flags.

Common situations: Writing [id.name=/_foo] and forgetting the trailing slash; editing a key and deleting the delimiter; assuming bare '/foo' works like a contains-match.

Related errors


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/6369f32e2fc043c1. Report an issue: GitHub.

Appendix: source

Thrown at cli/js/40_lint_selector.js:406

  switch (raw) {
    case "true":
      return true;
    case "false":
      return false;
    case "null":
      return null;
    case "undefined":
      return undefined;
    default:
      if (raw.startsWith("'") && raw.endsWith("'")) {
        if (raw.length === 2) return "";
        return raw.slice(1, -1);
      } else if (raw.startsWith('"') && raw.endsWith('"')) {
        if (raw.length === 2) return "";
        return raw.slice(1, -1);
      } else if (raw.startsWith("/")) {
        const end = raw.lastIndexOf("/");
        if (end === -1) throw new Error(`Invalid RegExp pattern: ${raw}`);
        const pattern = raw.slice(1, end);
        const flags = end < raw.length - 1 ? raw.slice(end + 1) : undefined;
        return new RegExp(pattern, flags);
      } else if (NUMBER_REG.test(raw)) {
        return Number(raw);
      } else if (BIGINT_REG.test(raw)) {
        return BigInt(raw.slice(0, -1));
      }

      return raw;
  }
}

export const ELEM_NODE = 1;
export const RELATION_NODE = 2;
export const ATTR_EXISTS_NODE = 3;
export const ATTR_BIN_NODE = 4;
export const PSEUDO_NTH_CHILD = 5;

View on GitHub (pinned to 9ad36f7a2c)