thedotmack/claude-mem · error

observation_search: "query" is required

Error message

observation_search: "query" is required

What it means

Validation error inside the observation_search tool handler. After requireServerForObservationTool() passes, the handler requires args.query to be a non-empty trimmed string and throws this plain Error otherwise. It fires before ServerClient.searchObservations() is called, so no /v1/search request leaves the process.

Source

Thrown at src/servers/mcp-server.ts:306

    ...(args.platformSource !== undefined ? { platformSource: normalizeMcpPlatformSource(args.platformSource) } : {}),
    ...(args.payload !== undefined ? { payload: args.payload } : {}),
    ...(args.generate !== undefined ? { generate: args.generate } : {}),
  };
  const response = await ctx.client.recordEvent(request);
  return formatJsonResult(response);
});

interface ObservationSearchArgs {
  projectId?: string;
  query: string;
  limit?: number;
  platformSource?: string | null;
}

const handleObservationSearch = wrapHandler('observation_search', async (args: ObservationSearchArgs) => {
  const ctx = requireServerForObservationTool('observation_search');
  if (typeof args?.query !== 'string' || args.query.trim().length === 0) {
    throw new Error('observation_search: "query" is required');
  }
  const projectId = args.projectId && args.projectId.trim().length > 0 ? args.projectId : ctx.projectId;
  const request: ServerSearchObservationsRequest = {
    projectId,
    query: args.query,
    ...(args.limit !== undefined ? { limit: args.limit } : {}),
    ...(args.platformSource !== undefined ? { platformSource: normalizeMcpPlatformSource(args.platformSource) } : {}),
  };
  const response = await ctx.client.searchObservations(request);
  return formatJsonResult(response);
});

interface ObservationContextArgs {
  projectId?: string;
  query: string;
  limit?: number;
  platformSource?: string | null;
}

View on GitHub (pinned to d768ba3643)

Solutions

  1. Pass a non-empty query string.
  2. If you want an unfiltered listing, use timeline or get_observations instead — observation_search requires a text query by design.
  3. Trim and length-check the query on the caller side before invoking.

Example fix

// before
await tools.observation_search({ limit: 20 });
// throws 'observation_search: "query" is required'

// after
await tools.observation_search({ query: 'ModeManager fallback', limit: 20 });
Defensive patterns

Strategy: validation

Validate before calling

if (typeof args?.query !== 'string' || args.query.trim().length === 0) {
  return { content: [{ type: 'text', text: 'query is required for observation_search' }], isError: true };
}

Type guard

function hasSearchQuery(v: unknown): v is { query: string; limit?: number } {
  return typeof (v as any)?.query === 'string' && (v as any).query.trim().length > 0;
}

Prevention

When it happens

Trigger: Calling observation_search with query omitted, null, non-string, or whitespace-only. The handler explicitly checks typeof args?.query === 'string' && args.query.trim().length > 0.

Common situations: Caller passes only limit/platformSource and forgets the query term; an LLM sends an empty query expecting 'list all'; query built from an empty template variable.

Related errors


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