{"record":{"id":"4498c2c146a5a589","repo":"JuliusBrussee/caveman","slug":"filter-monitor-must-be-an-object","errorCode":null,"errorMessage":"filter monitor must be an object","messagePattern":"filter monitor must be an object","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/agent-mcp.ts","lineNumber":490,"sourceCode":"      throw new Error(`filter ${key} must be a non-empty array of non-empty strings`);\n    }\n    if (key === \"status_class\" && value.some((entry) => ![\"2xx\", \"4xx\", \"5xx\"].includes(entry as string))) {\n      throw new Error(\"filter status_class values must be 2xx, 4xx, or 5xx\");\n    }\n  }\n  for (const key of [\"min_cost_usd\", \"max_cost_usd\", \"min_total_tokens\", \"max_total_tokens\", \"min_latency_ms\", \"max_latency_ms\"]) {\n    const value = filters[key];\n    if (value !== undefined && (typeof value !== \"number\" || !Number.isFinite(value))) {\n      throw new Error(`filter ${key} must be a finite number`);\n    }\n  }\n  for (const key of [\"has_error\", \"compressed\"]) {\n    const value = filters[key];\n    if (value !== undefined && typeof value !== \"boolean\") throw new Error(`filter ${key} must be a boolean`);\n  }\n  const monitor = filters.monitor;\n  if (monitor !== undefined) {\n    if (!monitor || typeof monitor !== \"object\" || Array.isArray(monitor)) throw new Error(\"filter monitor must be an object\");\n    const monitorUnknown = Object.keys(monitor).filter((key) => key !== \"id\" && key !== \"verdict\");\n    if (monitorUnknown.length > 0) throw new Error(`filter monitor has unknown key(s): ${monitorUnknown.sort().join(\", \")}`);\n    if (typeof monitor.id !== \"string\" || monitor.id.trim() === \"\") throw new Error(\"filter monitor.id is required\");\n    if (typeof monitor.verdict !== \"string\" || ![\"pass\", \"fail\", \"error\"].includes(monitor.verdict)) {\n      throw new Error(\"filter monitor.verdict must be pass, fail, or error\");\n    }\n  }\n}\n\nfunction optionalEnum(args: JSONObject, key: string, values: string[]): void {\n  const value = args[key];\n  if (value !== undefined && (typeof value !== \"string\" || !values.includes(value))) {\n    throw new Error(`${key} must be one of ${values.join(\", \")}`);\n  }\n}\n\nfunction validateTraceSearch(args: JSONObject): void {\n  validateTraceFilters(args);","sourceCodeStart":472,"sourceCodeEnd":508,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/agent-mcp.ts#L472-L508","documentation":"The monitor filter, when present, must be a plain object (not null or an array). Subsequent checks require it to contain exactly the keys id and verdict — this specific error fires for the outer shape check before the id/verdict validation.","triggerScenarios":"Passing filters: { monitor: null }, { monitor: [] }, { monitor: 'monitor-1' }, or any non-object value for the monitor key in trace search filters.","commonSituations":"Passing just the monitor id string instead of an object; JSON arrays of monitor criteria; null where an object was expected after a failed lookup.","solutions":["Pass monitor as an object: { id: '<monitor-id>', verdict: 'pass' | 'fail' | 'error' }","Convert a bare id into { id, verdict } if you have the verdict","Omit the monitor key when not filtering by monitor"],"exampleFix":"// before\nsearchTraces({ filters: { monitor: 'mon_123' } });\n// after\nsearchTraces({ filters: { monitor: { id: 'mon_123', verdict: 'fail' } } });","handlingStrategy":"type-guard","validationCode":"function assertMonitorFilter(filters) {\n  const monitor = filters?.monitor;\n  if (monitor === undefined) return;\n  if (monitor === null || typeof monitor !== 'object' || Array.isArray(monitor)) {\n    throw new Error('filter monitor must be an object: { id, verdict }');\n  }\n  if (typeof monitor.id !== 'string' || !monitor.id.trim()) throw new Error('filter monitor.id is required');\n  if (!['pass', 'fail', 'error'].includes(monitor.verdict)) throw new Error('invalid monitor.verdict');\n}","typeGuard":"function isMonitorFilter(v) {\n  return typeof v === 'object' && v !== null && !Array.isArray(v)\n    && typeof v.id === 'string' && v.id.trim() !== ''\n    && ['pass', 'fail', 'error'].includes(v.verdict);\n}","tryCatchPattern":"try {\n  await searchTraces({ filters });\n} catch (error) {\n  if (error.message === 'filter monitor must be an object') {\n    filters.monitor = { id: String(filters.monitor), verdict: 'fail' };\n  }\n}","preventionTips":["Always pass monitor as { id, verdict }, never a bare id string","Validate the monitor shape before the tool call","Omit the monitor key when not filtering by monitor","Type monitor filters explicitly instead of unknown/string"],"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"}