Stirling-Tools/Stirling-PDF · warning · Error
Expected )
Error message
Expected )
What it means
Thrown by parsePrimary when an opening '(' was consumed and a full disjunction parsed, but the next non-whitespace character is not ')'. This indicates an unbalanced or malformed parenthesized sub-expression. As with all ExpressionParser errors, the public parseSelection() swallows it via CSV fallback; it only surfaces through parseSelectionWithDiagnostics in strict mode.
Source
Thrown at frontend/editor/src/core/utils/bulkselection/parseSelection.ts:204
return complement(inner, this.max);
}
// Word-form NOT
if (this.tryConsumeNot()) {
const inner = this.parseUnary();
return complement(inner, this.max);
}
return this.parsePrimary();
}
private parsePrimary(): Set<number> {
this.skipWs();
// Parenthesized expression: '(' expression ')'
if (this.peek("(")) {
this.consume(1);
const inner = this.parseDisjunction();
this.skipWs();
if (!this.peek(")")) throw new Error("Expected )");
this.consume(1);
return inner;
}
// Keywords: even / odd
const keyword = this.tryReadKeyword();
if (keyword) {
if (keyword === "even") return this.buildEven();
if (keyword === "odd") return this.buildOdd();
}
// Progression: k n ( +/- c )?
const progression = this.tryReadProgression();
if (progression) {
return this.buildProgression(progression.k, progression.c);
}
// Number or RangeView on GitHub (pinned to 9ef20dcab8)
Solutions
- Count and balance parentheses in the input — every '(' needs a matching ')'.
- Use round parentheses only; '[' and '{' are not in the grammar.
- Enable strict mode only where you want to reject ambiguous input; otherwise rely on the CSV fallback and the returned warning.
- Provide a live syntax hint/preview so users see matching before submitting.
Example fix
// before
parseSelectionWithDiagnostics('(1-5 & odd', max, { strict: true })
// after
parseSelectionWithDiagnostics('(1-5 & odd)', max, { strict: true }) Defensive patterns
Strategy: validation
Validate before calling
function isBalancedParens(input: string): boolean {
let depth = 0;
for (const ch of input) {
if (ch === '(') depth++;
else if (ch === ')') depth--;
if (depth < 0) return false;
}
return depth === 0;
}
if (!isBalancedParens(input)) {
showUser('Parentheses are unbalanced.');
} Type guard
function usesOnlyRoundParens(input: string): boolean {
return !/[\[\]{}]/.test(input);
} Try / catch
try {
const { pages } = parseSelectionWithDiagnostics(input, maxPages, { strict: true });
} catch (e) {
if (e instanceof Error && e.message === 'Expected )') {
showUser('Missing a closing parenthesis.');
} else { throw e; }
} Prevention
- Balance parentheses in real time as the user types.
- Only use '(' and ')'; square and curly brackets are not supported.
- Use non-strict mode to fall back to CSV and warn instead of throwing.
- Highlight matching pairs in the UI to prevent imbalance.
When it happens
Trigger: Input like '(1-5' (missing close paren), '(1-5 , 7-10' (missing close paren before more tokens), or '(1-5] ' (wrong bracket type). Any '(' that is not matched by a ')' at the point the parser expects.
Common situations: User typed a parenthesized expression and forgot the closing ')', used square/curly brackets, or an autocomplete/template inserted an unbalanced paren. Strict mode surfaces it; non-strict mode falls back to CSV (which ignores parentheses entirely).
Related errors
- Expected primary
- Unexpected trailing input
- Expected number
- No team resolved yet
- No automation configuration provided
AI-assisted analysis of Stirling-Tools/Stirling-PDF@9ef20dcab8 (2026-08-13).
Data as JSON: /api/errors/29039a52399cb582.
Report an issue: GitHub.