{"record":{"id":"29039a52399cb582","repo":"Stirling-Tools/Stirling-PDF","slug":"expected","errorCode":null,"errorMessage":"Expected )","messagePattern":"Expected \\)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"frontend/editor/src/core/utils/bulkselection/parseSelection.ts","lineNumber":204,"sourceCode":"      return complement(inner, this.max);\n    }\n    // Word-form NOT\n    if (this.tryConsumeNot()) {\n      const inner = this.parseUnary();\n      return complement(inner, this.max);\n    }\n    return this.parsePrimary();\n  }\n\n  private parsePrimary(): Set<number> {\n    this.skipWs();\n\n    // Parenthesized expression: '(' expression ')'\n    if (this.peek(\"(\")) {\n      this.consume(1);\n      const inner = this.parseDisjunction();\n      this.skipWs();\n      if (!this.peek(\")\")) throw new Error(\"Expected )\");\n      this.consume(1);\n      return inner;\n    }\n\n    // Keywords: even / odd\n    const keyword = this.tryReadKeyword();\n    if (keyword) {\n      if (keyword === \"even\") return this.buildEven();\n      if (keyword === \"odd\") return this.buildOdd();\n    }\n\n    // Progression: k n ( +/- c )?\n    const progression = this.tryReadProgression();\n    if (progression) {\n      return this.buildProgression(progression.k, progression.c);\n    }\n\n    // Number or Range","sourceCodeStart":186,"sourceCodeEnd":222,"githubUrl":"https://github.com/Stirling-Tools/Stirling-PDF/blob/9ef20dcab80b85041912f045e17a6aea1d08f969/frontend/editor/src/core/utils/bulkselection/parseSelection.ts#L186-L222","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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."],"exampleFix":"// before\nparseSelectionWithDiagnostics('(1-5 & odd', max, { strict: true })\n// after\nparseSelectionWithDiagnostics('(1-5 & odd)', max, { strict: true })","handlingStrategy":"validation","validationCode":"function isBalancedParens(input: string): boolean {\n  let depth = 0;\n  for (const ch of input) {\n    if (ch === '(') depth++;\n    else if (ch === ')') depth--;\n    if (depth < 0) return false;\n  }\n  return depth === 0;\n}\n\nif (!isBalancedParens(input)) {\n  showUser('Parentheses are unbalanced.');\n}","typeGuard":"function usesOnlyRoundParens(input: string): boolean {\n  return !/[\\[\\]{}]/.test(input);\n}","tryCatchPattern":"try {\n  const { pages } = parseSelectionWithDiagnostics(input, maxPages, { strict: true });\n} catch (e) {\n  if (e instanceof Error && e.message === 'Expected )') {\n    showUser('Missing a closing parenthesis.');\n  } else { throw e; }\n}","preventionTips":["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."],"tags":["bulk-selection","parser","validation","syntax"],"backgroundTag":null,"analyzedSha":"9ef20dcab80b85041912f045e17a6aea1d08f969","analyzedAt":"2026-08-13T22:11:39.827Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}