thedotmack/claude-mem · error · AppError

INVALID_SEARCH_REQUEST

INVALID_SEARCH_REQUEST

Error message

Either query or filters required for search

What it means

searchObservations requires at least one of a text query or usable filters. When query is empty/absent and buildFilterClause produces no clause from the remaining options, SessionSearch throws this AppError with code INVALID_SEARCH_REQUEST and HTTP 400, because a SQL search with no constraints is not a valid request.

Solutions

  1. Provide a non-empty query string, e.g. searchObservations(db, 'login bug').
  2. If you want a filter-only search, pass at least one recognized filter (e.g. { project: 'myapp' } or a date range).
  3. Check filter key spelling and that values are non-empty — pagination options (limit/offset/orderBy) do not count as filters.
  4. If listing everything is intended, use a dedicated list/observations endpoint instead of search.

Example fix

// before
await search.searchObservations('', { limit: 10 });
// after
await search.searchObservations('', { limit: 10, project: 'myapp' });
Defensive patterns

Strategy: validation

Validate before calling

const hasFilters = Object.entries(rest).some(([k, v]) => !['limit','offset','orderBy'].includes(k) && v !== undefined && v !== null && v !== '');
if (!query && !hasFilters) throw new Error('Either query or filters required for search');

Try / catch

try {
  results = await search.searchObservations(query, options);
} catch (err) {
  if (err instanceof AppError && err.code === 'INVALID_SEARCH_REQUEST') {
    return respond(400, 'Provide a search query or at least one filter.');
  }
  throw err;
}

Prevention

When it happens

Trigger: searchObservations is called with query = ''/undefined and options whose filters (project, agent, date range, etc.) are all empty or unsupported, so buildFilterClause returns a falsy clause.

Common situations: A UI search box submitted blank with no active filters; API client sent only pagination options (limit/offset) which are destructured out of filters; filter keys were misspelled so none matched recognized filter fields.

Understand the failure class

Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.

Related errors


AI-assisted analysis of thedotmack/claude-mem@d8bc9755e7 (2026-09-17). Data as JSON: /api/errors/7c97a9c1f844b3c8. Report an issue: GitHub.

Appendix: source

Thrown at src/services/sqlite/SessionSearch.ts:287

      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 (SessionSearch.UNSEGMENTED_SCRIPT.test(query)) {
      const filterClause = this.buildFilterClause(filters, params, 'o');

View on GitHub (pinned to d8bc9755e7)