{"record":{"id":"e5f3b2a5cffd6f73","repo":"JuliusBrussee/caveman","slug":"filters-must-be-an-object","errorCode":null,"errorMessage":"filters must be an object","messagePattern":"filters must be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/agent-mcp.ts","lineNumber":448,"sourceCode":"  if (typeof candidate.status === \"number\") structuredContent.status = candidate.status;\n  return {\n    content: [{ type: \"text\", text: JSON.stringify(structuredContent) }],\n    structuredContent,\n    isError: true,\n  };\n}\n\nasync function withProjectQuery(client: AgentMcpClient, path: string): Promise<string> {\n  if (!PROJECT_QUERY_PATHS.has(path)) return path;\n  const projectId = await client.projectId();\n  const query = new URLSearchParams({ project_id: projectId });\n  return `${path}?${query}`;\n}\n\nfunction validateTraceFilters(args: JSONObject): void {\n  const filters = args.filters;\n  if (filters === undefined) return;\n  if (!filters || typeof filters !== \"object\" || Array.isArray(filters)) throw new Error(\"filters must be an object\");\n  const unknown = Object.keys(filters).filter((key) => !TRACE_FILTER_KEYS.has(key));\n  if (unknown.length > 0) throw new Error(`unknown trace filter(s): ${unknown.sort().join(\", \")}`);\n  const stringListKeys = [\n    \"workflow\",\n    \"agent\",\n    \"model\",\n    \"provider\",\n    \"error_code\",\n    \"auth_mode\",\n    \"runtime_mode\",\n    \"cache_status\",\n    \"session_id\",\n    \"client_user_hash\",\n    \"trace_id\",\n    \"member_user_id\",\n    \"api_key_id\",\n    \"optimization_id\",\n    \"status_class\",","sourceCodeStart":430,"sourceCodeEnd":466,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/agent-mcp.ts#L430-L466","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass filters as a plain object keyed by filter name","Convert array-of-pairs input into an object before calling","Omit filters entirely (or pass undefined) when there are no filters"],"exampleFix":"// before\nsearchTraces({ filters: [{ key: 'workflow', value: 'deploy' }] });\n// after\nsearchTraces({ filters: { workflow: ['deploy'] } });","handlingStrategy":"type-guard","validationCode":"function assertFiltersObject(filters) {\n  if (filters === undefined) return;\n  if (filters === null || typeof filters !== 'object' || Array.isArray(filters)) {\n    throw new Error('filters must be a plain object');\n  }\n}\nassertFiltersObject(args.filters);","typeGuard":"function isPlainObject(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v);\n}","tryCatchPattern":"try {\n  await searchTraces({ filters });\n} catch (error) {\n  if (error.message === 'filters must be an object') {\n    filters = Object.fromEntries(filters.map((f) => [f.key, f.value]));\n  }\n}","preventionTips":["Model filters as a keyed object, never an array of pairs","Type filters as Record<string, unknown> in client code","Validate filter input shape before the tool call","Normalize query-string filters into objects at the boundary"],"tags":["mcp","validation","traces","type-error"],"backgroundTag":"type-mismatch","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}