{"record":{"id":"0079f788ac5d2c85","repo":"tobi/qmd","slug":"location-lex-error","errorCode":null,"errorMessage":"${location} (lex): ${error}","messagePattern":"(.+?) \\(lex\\): (.+?)","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/store.ts","lineNumber":5888,"sourceCode":"  const explain = options?.explain ?? false;\n  const intent = options?.intent;\n  const skipRerank = options?.skipRerank ?? false;\n  const hooks = options?.hooks;\n\n  const collections = options?.collections;\n\n  if (searches.length === 0) return [];\n\n  // Validate queries before executing\n  for (const search of searches) {\n    const location = search.line ? `Line ${search.line}` : 'Structured search';\n    if (/[\\r\\n]/.test(search.query)) {\n      throw new Error(`${location} (${search.type}): queries must be single-line. Remove newline characters.`);\n    }\n    if (search.type === 'lex') {\n      const error = validateLexQuery(search.query);\n      if (error) {\n        throw new Error(`${location} (lex): ${error}`);\n      }\n    } else if (search.type === 'vec' || search.type === 'hyde') {\n      const error = validateSemanticQuery(search.query);\n      if (error) {\n        throw new Error(`${location} (${search.type}): ${error}`);\n      }\n    }\n  }\n\n  const rankedLists: RankedResult[][] = [];\n  const rankedListMeta: RankedListMeta[] = [];\n  const docidMap = new Map<string, string>(); // filepath -> docid\n  const hasVectors = !!store.db.prepare(\n    `SELECT name FROM sqlite_master WHERE type='table' AND name='vectors_vec'`\n  ).get();\n\n  // Helper to run search across collections (or all if undefined)\n  const collectionList = collections ?? [undefined]; // undefined = all collections","sourceCodeStart":5870,"sourceCodeEnd":5906,"githubUrl":"https://github.com/tobi/qmd/blob/dbfd0b4736aeaf761d1a16ca8e424f071df8feb9/src/store.ts#L5870-L5906","documentation":"For search entries of type 'lex', the query is run through validateLexQuery() and the returned error string is re-thrown prefixed with the location. This covers FTS5/lex syntax problems other than newlines — e.g. unbalanced quotes or disallowed operators.","triggerScenarios":"Passing a lex query like `\"unclosed phrase` or operators the lexer grammar rejects to searchMulti(); validateLexQuery returns an error message which is thrown verbatim.","commonSituations":"User-supplied raw FTS5 syntax without escaping; stray double quotes; copied SQL-ish queries with unsupported syntax.","solutions":["Use the lex query escaping helpers / wrap user phrases in double quotes properly","Show validateLexQuery's message to the user and let them fix the query","Fall back to a plain sanitized term list"],"exampleFix":"// before\nsearchMulti([{ type: 'lex', query: '\"unclosed' }]);\n// after\nconst err = validateLexQuery(q);\nif (err) throw new Error(`bad query: ${err}`);\nsearchMulti([{ type: 'lex', query: sanitize(q) }]);","handlingStrategy":"validation","validationCode":"const err = validateLexQuery(q); if (!err) searchMulti([{ type: 'lex', query: q }]); else report(err);","typeGuard":null,"tryCatchPattern":"try { searchMulti(s); } catch (e) { if (/\\(lex\\)/.test((e as Error).message)) return friendlyError(e); throw e; }","preventionTips":["Escape/quote user phrases for FTS5","Run validateLexQuery on raw user input early","Provide query syntax hints in error UI"],"tags":["query","lex","fts5","validation"],"backgroundTag":"query-syntax-error","analyzedSha":"dbfd0b4736aeaf761d1a16ca8e424f071df8feb9","analyzedAt":"2026-08-28T18:07:46.628Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}