thedotmack/claude-mem · warning · AppError
INVALID_SEARCH_REQUEST
INVALID_SEARCH_REQUEST
Error message
Either query or filters required for search
What it means
searchObservations() requires either a non-empty query or at least one usable filter. When query is falsy it builds a filter clause from the remaining options; if that clause is empty (no recognized filters contributed), it throws AppError 400 INVALID_SEARCH_REQUEST. This prevents an unbounded SELECT returning the whole observations table.
Source
Thrown at src/services/sqlite/SessionSearch.ts:254
case 'relevance':
return hasFTS ? `ORDER BY ${ftsTable}.rank ASC` : 'ORDER BY o.created_at_epoch DESC';
case 'date_desc':
return 'ORDER BY o.created_at_epoch DESC';
case 'date_asc':
return 'ORDER BY o.created_at_epoch ASC';
default:
return 'ORDER BY o.created_at_epoch DESC';
}
}
searchObservations(query: string | undefined, options: SearchOptions = {}): ObservationSearchResult[] {
const params: any[] = [];
const { limit = 50, offset = 0, orderBy = 'relevance', ...filters } = options;
if (!query) {
const filterClause = this.buildFilterClause(filters, params, 'o');
if (!filterClause) {
throw new AppError(SessionSearch.MISSING_SEARCH_INPUT_MESSAGE, 400, 'INVALID_SEARCH_REQUEST');
}
const orderClause = this.buildOrderClause(orderBy, false);
const sql = `
SELECT o.*, o.discovery_tokens
FROM observations o
WHERE ${filterClause}
${orderClause}
LIMIT ? OFFSET ?
`;
params.push(limit, offset);
return this.db.prepare(sql).all(...params) as ObservationSearchResult[];
}
if (this._fts5Available) {
const filterClause = this.buildFilterClause(filters, params, 'o');View on GitHub (pinned to d768ba3643)
Solutions
- Provide a non-empty query string, OR at least one filter the buildFilterClause recognizes (e.g. project, platformSource, dateRange, type).
- In calling UI, disable the search action until the user enters a query or picks a filter.
- Validate inputs before calling: if neither query nor filters are present, return [] without invoking search.
Example fix
// before
search.searchObservations(undefined, { limit: 50 }); // throws
// after
if (!query && Object.keys(filters).length === 0) return [];
search.searchObservations(query, { ...filters, limit: 50 }); Defensive patterns
Strategy: validation
Validate before calling
import type { SearchOptions } from './types.js';
function hasSearchInput(query?: string, options: SearchOptions = {}): boolean {
if (query && query.trim()) return true;
const { limit, offset, orderBy, ...filters } = options;
return Object.keys(filters).length > 0;
}
// usage
if (!hasSearchInput(query, options)) return [];
search.searchObservations(query, options); Type guard
import { AppError } from '../server/ErrorHandler.js';
function isInvalidSearchRequest(e: unknown): boolean {
return e instanceof AppError && e.code === 'INVALID_SEARCH_REQUEST';
} Try / catch
try {
return search.searchObservations(query, options);
} catch (e) {
if (e instanceof AppError && e.code === 'INVALID_SEARCH_REQUEST') return []; // empty input -> empty result
throw e;
} Prevention
- Require a query or at least one filter in the calling UI before issuing search.
- Do not pass bare {limit,offset,orderBy} — those are destructured out and do not count as filters.
- Share a hasSearchInput helper across all three search entry points.
When it happens
Trigger: Calling searchObservations(undefined, {}) or searchObservations('', { limit: 10 }) — i.e. no query and no filters (or only keys buildFilterClause ignores, plus limit/offset/orderBy which are destructured out).
Common situations: A caller passes an empty search box with no filters selected; a default invocation from a UI that sends {limit,offset} only; a programmatic caller that conditionally omits both query and filters.
Related errors
- observation_search: "query" is required
- Invalid CLAUDE_MEM_WORKER_PORT in settings.json: missing
- Invalid CLAUDE_MEM_WORKER_PORT in settings.json: ${rawPort}
- Unknown Claude model: ${options.model}. Allowed: ${[...allow
- CLAUDE_MEM_QUEUE_ENGINE is not "bullmq"
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/7c97a9c1f844b3c8.
Report an issue: GitHub.