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_tokens

View on GitHub (pinned to e2d1df569a)

Solutions

  1. Check for the companion 'FTS5 table creation failed' warning in the same startup log
  2. Verify the sessions FTS table exists in sqlite_master
  3. Recreate the FTS tables (drop + restart) if missing or corrupt
  4. Mitigate SQLITE_BUSY with fewer concurrent writers or a higher busy_timeout
  5. 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

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


AI-assisted analysis of thedotmack/claude-mem@e2d1df569a (2026-08-20). Data as JSON: /api/errors/b115e4efd83fa886. Report an issue: GitHub.