thedotmack/claude-mem · error
FTS5 session search failed
Error message
FTS5 session search failed
What it means
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.
Source
Thrown at src/services/sqlite/SessionSearch.ts:357
const sql = `
SELECT s.*, s.discovery_tokens
FROM session_summaries s
JOIN session_summaries_fts ON session_summaries_fts.rowid = s.id
WHERE session_summaries_fts MATCH ?
${filterClause ? 'AND ' + filterClause : ''}
${orderClause}
LIMIT ? OFFSET ?
`;
const escapedQuery = '"' + query.replace(/"/g, '""') + '"';
params.unshift(escapedQuery);
params.push(limit, offset);
try {
return this.db.prepare(sql).all(...params) as SessionSummarySearchResult[];
} catch (error) {
logger.warn('DB', 'FTS5 session search failed', {}, error instanceof Error ? error : undefined);
throw error;
}
}
logger.warn('DB', 'Text search unavailable: ChromaDB disabled and FTS5 not available');
return [];
}
findByConcept(concept: string, options: SearchOptions = {}): ObservationSearchResult[] {
const params: any[] = [];
const { limit = 50, offset = 0, orderBy = 'date_desc', ...filters } = options;
const conceptFilters = { ...filters, concepts: concept };
const filterClause = this.buildFilterClause(conceptFilters, params, 'o');
const orderClause = this.buildOrderClause(orderBy, false);
const sql = `
SELECT o.*, o.discovery_tokensView on GitHub (pinned to e2d1df569a)
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
Example fix
// before
const results = search.searchSessions(q, { limit: 50 });
// after — survive FTS failures at the UI boundary
let results: SessionSummarySearchResult[];
try {
results = search.searchSessions(q, { limit: 50 });
} catch (err) {
logger.warn('DB', 'FTS session search failed; using fallback', {}, err instanceof Error ? err : undefined);
results = search.searchSessions(undefined, { limit: 50 });
} Defensive patterns
Strategy: try-catch
Validate before calling
const hasFts = !!db
.prepare(`SELECT name FROM sqlite_master WHERE type = 'table' AND name LIKE '%fts%'`)
.get();
if (!hasFts) {
// degrade to ChromaDB or LIKE before calling searchSessions
} Try / catch
try {
results = search.searchSessions(q, { limit });
} catch (err) {
logger.warn('DB', 'FTS session search failed; using fallback', {}, err instanceof Error ? err : undefined);
results = search.searchSessions(undefined, { limit });
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: FTS init failed in a previous run leaving _fts5Available stale; manual deletion of the sessions FTS table; heavy concurrent writes during search.
Related errors
- FTS5 observation search failed
- FTS5 table creation failed — search will use ChromaDB and LI
- FTS5 not available — user_prompts_fts skipped (search uses C
- Invalid CLAUDE_MEM_QUEUE_ENGINE=${raw}; expected sqlite or b
- cloud sync canonical payload: ${name} must be a non-negative
AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20).
Data as JSON: /api/errors/b115e4efd83fa886.
Report an issue: GitHub.