denoland/deno · error · Error

Unknown pseudo selector: '${lex.value}'

Error message

Unknown pseudo selector: '${lex.value}'

What it means

Thrown by the pseudo-selector switch default (cli/js/40_lint_selector.js) when a ':' pseudo-class is encountered that this parser does not implement. It recognizes a fixed set (including :not(), :has(), :is(), :nth-child()); anything else — with or without arguments — hits the default and throws with the offending name.

Source

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

          stack.push([]);

          continue;
        }
        case "not": {
          lex.next();
          lex.expect(Token.BraceOpen);
          lex.next();

          current.push({
            type: PSEUDO_NOT,
            selectors: [],
          });
          stack.push([]);

          continue;
        }
        default:
          throw new Error(`Unknown pseudo selector: '${lex.value}'`);
      }
    } else if (lex.token === Token.Comma) {
      if (throwOnComma) {
        throw new Error(`Multiple selector arguments not supported here`);
      }

      lex.next();
      if (lex.token === Token.Space) {
        lex.next();
      }

      popSelector(result, stack);
      stack.push([]);
      continue;
    } else if (lex.token === Token.BraceClose) {
      throwOnComma = false;
      popSelector(result, stack);
    } else if (lex.token === Token.Op) {

View on GitHub (pinned to 89f33cbef2)

Solutions

  1. Replace the unsupported pseudo with the equivalent supported construct (:matches → :is)
  2. Express structural conditions inside the visitor callback (track child index yourself) when no pseudo exists
  3. Check the exact pseudo name for typos against the supported list

Example fix

// before
":matches(CallExpression, NewExpression)"(node) {},

// after
":is(CallExpression, NewExpression)"(node) {},
Defensive patterns

Strategy: validation

Validate before calling

const SUPPORTED_PSEUDOS = new Set(["has", "is", "not", "nth-child" /* + first/last-child if used */]);
function checkPseudos(sel) {
  for (const m of sel.matchAll(/:([a-zA-Z-]+)/g)) {
    if (!SUPPORTED_PSEUDOS.has(m[1])) {
      throw new Error(`unsupported pseudo ':${m[1]}' in '${sel}'`);
    }
  }
}

Type guard

function usesSupportedPseudos(sel) {
  return [...sel.matchAll(/:([a-zA-Z-]+)/g)].every((m) =>
    SUPPORTED_PSEUDOS.has(m[1])
  );
}

Prevention

When it happens

Trigger: Visitor-key selectors using unsupported pseudos: ":matches(X)", ":root", ":first-of-type", ":empty", ":statement" — any ':' word not in the supported set. Note :not() takes brace-enclosed arguments here, and unsupported pseudos throw even if they are valid CSS or esquery.

Common situations: Copying a selector from esquery documentation assuming full parity; using CSS structural pseudos (:first-child variants beyond what's supported) on AST nodes; typos like ':hass(...)' or ':nota(...)' that fall through to the default.

Related errors


AI-assisted analysis of denoland/deno@89f33cbef2 (2026-08-16). Data as JSON: /api/errors/84b3d3ae46d3f1df. Report an issue: GitHub.