JuliusBrussee/caveman · error

filters must be an object

Error message

filters must be an object

What it means

validateTraceFilters checks the optional filters argument of trace search tools. If filters is provided it must be a plain (non-null, non-array) object; anything else — null, a scalar, or an array — throws "filters must be an object" before any further filter-key validation runs.

Solutions

  1. Pass filters as a plain object keyed by filter name
  2. Convert array-of-pairs input into an object before calling
  3. Omit filters entirely (or pass undefined) when there are no filters

Example fix

// before
searchTraces({ filters: [{ key: 'workflow', value: 'deploy' }] });
// after
searchTraces({ filters: { workflow: ['deploy'] } });
Defensive patterns

Strategy: type-guard

Validate before calling

function assertFiltersObject(filters) {
  if (filters === undefined) return;
  if (filters === null || typeof filters !== 'object' || Array.isArray(filters)) {
    throw new Error('filters must be a plain object');
  }
}
assertFiltersObject(args.filters);

Type guard

function isPlainObject(v) {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

Try / catch

try {
  await searchTraces({ filters });
} catch (error) {
  if (error.message === 'filters must be an object') {
    filters = Object.fromEntries(filters.map((f) => [f.key, f.value]));
  }
}

Prevention

When it happens

Trigger: Passing filters: null, filters: [], filters: "workflow=foo", or filters: 42 to the trace search tool; arrays are explicitly rejected even though they pass a naive typeof check.

Common situations: Serializing filters from a query string into an array; JSON schemas allowing any type so the model sends an array of {key,value} pairs; frontends building filters as a list of conditions.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/e5f3b2a5cffd6f73. Report an issue: GitHub.

Appendix: source

Thrown at packages/cli/src/agent-mcp.ts:448

  if (typeof candidate.status === "number") structuredContent.status = candidate.status;
  return {
    content: [{ type: "text", text: JSON.stringify(structuredContent) }],
    structuredContent,
    isError: true,
  };
}

async function withProjectQuery(client: AgentMcpClient, path: string): Promise<string> {
  if (!PROJECT_QUERY_PATHS.has(path)) return path;
  const projectId = await client.projectId();
  const query = new URLSearchParams({ project_id: projectId });
  return `${path}?${query}`;
}

function validateTraceFilters(args: JSONObject): void {
  const filters = args.filters;
  if (filters === undefined) return;
  if (!filters || typeof filters !== "object" || Array.isArray(filters)) throw new Error("filters must be an object");
  const unknown = Object.keys(filters).filter((key) => !TRACE_FILTER_KEYS.has(key));
  if (unknown.length > 0) throw new Error(`unknown trace filter(s): ${unknown.sort().join(", ")}`);
  const stringListKeys = [
    "workflow",
    "agent",
    "model",
    "provider",
    "error_code",
    "auth_mode",
    "runtime_mode",
    "cache_status",
    "session_id",
    "client_user_hash",
    "trace_id",
    "member_user_id",
    "api_key_id",
    "optimization_id",
    "status_class",

View on GitHub (pinned to 3ee70a1026)