amruthpillai/reactive-resume · error · Error

Invalid :${name} expression.

Error message

Invalid :${name} expression.

What it means

Thrown by compileNth when an AnPlusB expression has an a or b component that Number() cannot coerce to an integer (Number.isInteger is false). Non-integer or non-numeric steps are rejected so the matcher stays deterministic.

Source

Thrown at packages/resume/src/stylesheet/selector.ts:168

	}

	const expression = nth[0].nth;
	let a = 0;
	let b = 0;
	if (expression.type === "Identifier") {
		const keyword = astName(expression).toLowerCase();
		if (keyword === "odd") {
			a = 2;
			b = 1;
		} else if (keyword === "even") {
			a = 2;
		} else {
			throw new Error(`Unsupported :${name} expression.`);
		}
	} else if (expression.type === "AnPlusB") {
		a = expression.a === null || expression.a === undefined ? 0 : Number(expression.a);
		b = expression.b === null || expression.b === undefined ? 0 : Number(expression.b);
		if (!Number.isInteger(a) || !Number.isInteger(b)) throw new Error(`Invalid :${name} expression.`);
	} else {
		throw new Error(`Unsupported :${name} expression.`);
	}

	if (name === "nth-of-type" && nth[0].selector) throw new Error(":nth-of-type does not accept an of selector.");
	const of = nth[0].selector ? compileSelectorList(nth[0].selector, { depth: context.depth + 1 }) : undefined;
	return { type: "pseudo", name, a, b, ...(of ? { of } : {}) };
}

function compileSimple(node: SelectorAst, context: CompileContext): CompiledSimpleSelector | null {
	switch (node.type) {
		case "TypeSelector": {
			const name = astName(node);
			if (name === "*") return { type: "universal" };
			if (!knownKinds.has(name)) throw new Error(`Unknown semantic element ${name}.`);
			return { type: "type", name: name as SemanticNode["kind"] };
		}
		case "IdSelector":

View on GitHub (pinned to 3a5b12e2a4)

Solutions

  1. Use integer coefficients only: :nth-child(2n+1), :nth-child(3n).
  2. Drop the fractional part: 1.5n becomes 1n or 2n depending on intent.
  3. Validate the coefficient before interpolating it into a generated selector.

Example fix

// before
item:nth-child(1.5n+1)
// after
item:nth-child(2n+1)
Defensive patterns

Strategy: validation

Validate before calling

const integerCoefficients = (a, b) =>
  (a === '' || a == null || Number.isInteger(Number(a))) &&
  (b === '' || b == null || Number.isInteger(Number(b)));
// verify before interpolating into :nth-child(<a>n+<b>)

Type guard

const isRejected = (r) => r.selector === null && typeof r.error === 'string';

Try / catch

const result = compileSelector(selector);
if (result.selector === null && result.error.startsWith('Invalid :')) {
  roundCoefficientToInt(selector);
}

Prevention

When it happens

Trigger: Writing :nth-child(1.5n), :nth-child(n.5), or any An+B whose coefficients parse to a non-integer like NaN.

Common situations: Typing a decimal step; css-tree accepting a syntactically valid but semantically non-integer coefficient.

Related errors


AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12). Data as JSON: /api/errors/eb33fce6c884821e. Report an issue: GitHub.