denoland/deno · error · Error

Empty selector

Error message

Empty selector

What it means

popSelector (cli/js/40_lint_selector.js) attaches each finished selector group to its parent node; when the group below the popped frame is empty it throws `Empty selector` as an invariant guard. Under the current grammar most user inputs route elsewhere (empty groups inside :is()/:has() are tolerated), so encountering this error usually means a degenerate selector key or a parser edge case — notably :nth-child forms without an `of` clause interact oddly with the parser's group stack.

Solutions

  1. Simplify the selector key: reduce it to node types and one pseudo-class at a time until the throw disappears.
  2. Avoid mixing :nth-child(...) (without `of`) inside :has()/:is()/:not() compound selectors; compute the check in JavaScript instead.
  3. If the key looks valid, minimize it and report the case to the Deno repository (denoland/deno) — this branch is an invariant guard and may indicate a parser bug.
Defensive patterns

Strategy: validation

Prevention

When it happens

Trigger: Degenerate or malformed selector keys that leave an empty selector group on the stack when one is popped — e.g. empty groups created by stray separators, or unusual :nth-child(...) placements inside :has()/:is()/:not(). Well-formed keys effectively never reach this branch.

Common situations: Hand-crafted selector keys assembled by string concatenation that produce empty segments; experimental selector syntax; a key that looks valid but trips a parser bug.

Related errors


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

Appendix: source

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

  }

  return result;
}

/**
 * @param {Selector[]} result
 * @param {Selector[]} stack
 */
function popSelector(result, stack) {
  const sel = /** @type {Selector} */ (stack.pop());

  if (stack.length === 0) {
    result.push(sel);
    stack.push([]);
  } else {
    const prev = /** @type {Selector} */ (stack.at(-1));
    if (prev.length === 0) {
      throw new Error(`Empty selector`);
    }

    const node = prev.at(-1);
    if (node === undefined) {
      throw new Error(`Empty node`);
    }

    if (node.type === PSEUDO_NTH_CHILD) {
      node.of = sel;
    } else if (
      node.type === PSEUDO_HAS || node.type === PSEUDO_IS ||
      node.type === PSEUDO_NOT
    ) {
      node.selectors.push(sel);
    } else {
      throw new Error(`Multiple selectors not allowed here`);
    }
  }

View on GitHub (pinned to 9ad36f7a2c)