denoland/deno · error · Error

Expected 'of' keyword in ':nth-child' but got

Error message

Expected 'of' keyword in ':nth-child' but got: ${lex.value}

What it means

The selector engine implements the CSS-style 'An+B of S' form: :nth-child(<step>[+/-offset] of <selector>). After reading the step/offset, the only keyword it accepts inside the parentheses is `of` (cli/js/40_lint_selector.js); any other word there throws `Expected 'of' keyword in ':nth-child' but got: <word>`, naming the offending token.

Solutions

  1. Use the `of` keyword when you want to restrict the counted siblings: ":nth-child(2 of Expression)".
  2. Remove the stray word if no restriction was intended: ":nth-child(2 3)" → ":nth-child(2)".
  3. Check the exact word named in the error message — it is the token the parser choked on.

Example fix

// before
"Statement:nth-child(2 x)"(node) { /* ... */ },

// after
"Statement:nth-child(2 of Expression)"(node) { /* ... */ },
Defensive patterns

Strategy: validation

Validate before calling

function checkNthChildKeys(key) {
  const m = key.match(/:nth-child\(\s*[0-9n+-]+\s+([a-z]+)/i);
  if (m && m[1] !== "of") {
    throw new Error(`Expected 'of' in "${key}" but got '${m[1]}'`);
  }
}

Prevention

When it happens

Trigger: Visitor keys like "Statement:nth-child(2 3)" or "Expression:nth-child(2n+1 even)" — after the numeric argument, a word other than "of" appears before the closing parenthesis. Correct form: ":nth-child(2 of VariableDeclaration)".

Common situations: Transcribing CSS An+B syntax incorrectly (e.g. '2n+1 even' confusion); leaving a stray identifier after the step; attempting to pass a second plain argument to :nth-child.

Related errors


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

Appendix: source

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

            } else if (lex.token === Token.Minus) {
              node.op = "-";
              lex.next();

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

            lex.expect(Token.Word);
            node.stepOffset = +lex.value;
            lex.next();

            if (lex.token !== Token.BraceClose) {
              lex.next(); // Space

              if (lex.token === Token.Word) {
                if (/** @type {string} */ (lex.value) !== "of") {
                  throw new Error(
                    `Expected 'of' keyword in ':nth-child' but got: ${lex.value}`,
                  );
                }

                lex.next();
                lex.expect(Token.Space);
                lex.next();
                throwOnComma = true;
                stack.push([]);
              }

              continue;
            }

            lex.expect(Token.BraceClose);
          } else if (!node.repeat) {
            // :nth-child(2) -> step is actually stepOffset
            node.stepOffset = node.step - 1;

View on GitHub (pinned to 9ad36f7a2c)