amruthpillai/reactive-resume · error · Error
Attribute selector matcher and value must be used together.
Error message
Attribute selector matcher and value must be used together.
What it means
Thrown by compileSimple in the AttributeSelector branch when a matcher and a value are not both present or both absent (the XOR of 'matcher is null/undefined' and 'value is null' is true). A bare [attr=val] needs both; a presence [attr] needs neither.
Source
Thrown at packages/resume/src/stylesheet/selector.ts:198
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":
return { type: "id", value: astName(node) };
case "AttributeSelector": {
const name = astName(node);
if (!knownAttributes.has(name)) throw new Error(`Unknown semantic attribute ${name}.`);
if (node.flags) throw new Error("Attribute selector flags are not supported.");
const matcher = node.matcher as AttributeMatcher | null | undefined;
if (matcher !== null && matcher !== undefined && !["=", "~=", "|=", "^=", "$=", "*="].includes(matcher)) {
throw new Error(`Unsupported attribute matcher ${matcher}.`);
}
const value = attributeValue(node);
if ((matcher === null || matcher === undefined) !== (value === null)) {
throw new Error("Attribute selector matcher and value must be used together.");
}
return { type: "attribute", name, matcher: matcher ?? null, value };
}
case "PseudoClassSelector": {
const name = astName(node);
if (["root", "first-child", "last-child", "only-child"].includes(name)) {
if (node.children !== null && node.children !== undefined)
throw new Error(`:${name} does not accept arguments.`);
return { type: "pseudo", name: name as "root" | "first-child" | "last-child" | "only-child" };
}
if (["is", "where", "not"].includes(name)) {
if (context.depth >= SEMANTIC_CSS_LIMITS_V1.maxFunctionDepth) {
throw new Error("Selector function nesting is too deep.");
}
const nested = childrenOf(node);
if (nested.length !== 1 || nested[0]?.type !== "SelectorList") {
throw new Error(`:${name} requires a selector list.`);
}View on GitHub (pinned to 3a5b12e2a4)
Solutions
- For a value comparison, include both: [type='experience'].
- For presence only, include neither: [type].
- Validate the (matcher, value) pairing before interpolating into a selector string.
Example fix
// before
// AST: { name:'type', matcher:'=', value:null }
// after
[type='experience'] Defensive patterns
Strategy: validation
Validate before calling
const matcherAndValuePaired = (matcher, value) => (matcher === null || matcher === undefined) === (value === null); // verify before constructing an AttributeSelector node
Type guard
const isRejected = (r) => r.selector === null && typeof r.error === 'string';
Try / catch
const result = compileSelector(selectorOrAst);
if (result.selector === null && result.error === 'Attribute selector matcher and value must be used together.') {
addMissingValueOrMatcher();
} Prevention
- Always pair a matcher with a value, or omit both for a presence test.
- Validate templated selector fragments for half-built attribute selectors.
- Prefer authoring selectors as strings over building AST by hand.
When it happens
Trigger: A programmatically built AST with a matcher but no value, or a value but no matcher; malformed selector fragments produced by string concatenation.
Common situations: Templating selector strings and forgetting the value; constructing CssNode objects manually; partial regex substitutions on selectors.
Related errors
- Unsupported attribute matcher ${matcher}.
- Attribute ${selector.name} is not available on ${type}.
- Unknown semantic attribute ${name}.
- :${name} requires a selector list.
- Unsupported selector node ${node.type}.
AI-assisted analysis of amruthpillai/reactive-resume@3a5b12e2a4 (2026-08-12).
Data as JSON: /api/errors/e780d908e171de39.
Report an issue: GitHub.