{"record":{"id":"b115e4efd83fa886","repo":"thedotmack/claude-mem","slug":"fts5-session-search-failed","errorCode":null,"errorMessage":"FTS5 session search failed","messagePattern":"FTS5 session search failed","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/services/sqlite/SessionSearch.ts","lineNumber":357,"sourceCode":"\n      const sql = `\n        SELECT s.*, s.discovery_tokens\n        FROM session_summaries s\n        JOIN session_summaries_fts ON session_summaries_fts.rowid = s.id\n        WHERE session_summaries_fts MATCH ?\n        ${filterClause ? 'AND ' + filterClause : ''}\n        ${orderClause}\n        LIMIT ? OFFSET ?\n      `;\n\n      const escapedQuery = '\"' + query.replace(/\"/g, '\"\"') + '\"';\n      params.unshift(escapedQuery);\n      params.push(limit, offset);\n\n      try {\n        return this.db.prepare(sql).all(...params) as SessionSummarySearchResult[];\n      } catch (error) {\n        logger.warn('DB', 'FTS5 session search failed', {}, error instanceof Error ? error : undefined);\n        throw error;\n      }\n    }\n\n    logger.warn('DB', 'Text search unavailable: ChromaDB disabled and FTS5 not available');\n    return [];\n  }\n\n  findByConcept(concept: string, options: SearchOptions = {}): ObservationSearchResult[] {\n    const params: any[] = [];\n    const { limit = 50, offset = 0, orderBy = 'date_desc', ...filters } = options;\n\n    const conceptFilters = { ...filters, concepts: concept };\n    const filterClause = this.buildFilterClause(conceptFilters, params, 'o');\n    const orderClause = this.buildOrderClause(orderBy, false);\n\n    const sql = `\n      SELECT o.*, o.discovery_tokens","sourceCodeStart":339,"sourceCodeEnd":375,"githubUrl":"https://github.com/thedotmack/claude-mem/blob/e2d1df569a8f04075d40e92461128ece7cf04c82/src/services/sqlite/SessionSearch.ts#L339-L375","documentation":"An FTS5 MATCH query against the session-summaries full-text index threw. SessionSearch logs this warning and rethrows, so callers of searchSessions() receive the raw SQLite error. Same failure family as the observations variant: missing/corrupt FTS table, locked DB, or an index that was never created at startup.","triggerScenarios":"Calling searchSessions(query, ...) with a non-empty query while the sessions FTS table or triggers are missing or corrupt; SQLITE_BUSY from a concurrent writer; a read-only database.","commonSituations":"FTS init failed in a previous run leaving _fts5Available stale; manual deletion of the sessions FTS table; heavy concurrent writes during search.","solutions":["Check for the companion 'FTS5 table creation failed' warning in the same startup log","Verify the sessions FTS table exists in sqlite_master","Recreate the FTS tables (drop + restart) if missing or corrupt","Mitigate SQLITE_BUSY with fewer concurrent writers or a higher busy_timeout","Catch at the call site and fall back to ChromaDB/LIKE search"],"exampleFix":"// before\nconst results = search.searchSessions(q, { limit: 50 });\n\n// after — survive FTS failures at the UI boundary\nlet results: SessionSummarySearchResult[];\ntry {\n  results = search.searchSessions(q, { limit: 50 });\n} catch (err) {\n  logger.warn('DB', 'FTS session search failed; using fallback', {}, err instanceof Error ? err : undefined);\n  results = search.searchSessions(undefined, { limit: 50 });\n}","handlingStrategy":"try-catch","validationCode":"const hasFts = !!db\n  .prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE '%fts%'`)\n  .get();\nif (!hasFts) {\n  // degrade to ChromaDB or LIKE before calling searchSessions\n}","typeGuard":null,"tryCatchPattern":"try {\n  results = search.searchSessions(q, { limit });\n} catch (err) {\n  logger.warn('DB', 'FTS session search failed; using fallback', {}, err instanceof Error ? err : undefined);\n  results = search.searchSessions(undefined, { limit });\n}","preventionTips":["Gate session text search on the FTS init success log, not on assumptions","Keep a single writer per DB file to avoid busy-database MATCH failures","Recreate FTS tables promptly when they go missing so searches do not throw"],"tags":["sqlite","fts5","full-text-search","rethrow"],"backgroundTag":"fts5-query-failed","analyzedSha":"e2d1df569a8f04075d40e92461128ece7cf04c82","analyzedAt":"2026-08-20T23:58:13.836Z","contentChangedAt":"2026-08-20T23:58:13.836Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}