{"record":{"id":"8f376c2db4fdfaab","repo":"JuliusBrussee/caveman","slug":"filter-monitor-id-is-required","errorCode":null,"errorMessage":"filter monitor.id is required","messagePattern":"filter monitor\\.id is required","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/cli/src/agent-mcp.ts","lineNumber":493,"sourceCode":"      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);\n  for (const key of [\"from\", \"to\"]) {\n    const value = args[key];\n    if (value !== undefined && typeof value !== \"string\") throw new Error(`${key} must be a string`);","sourceCodeStart":475,"sourceCodeEnd":511,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/766dce6b1394ebb56a3090748d5a0240a5aefb36/packages/cli/src/agent-mcp.ts#L475-L511","documentation":"Thrown by the caveman agent MCP server (agent-mcp.ts) while validating a caveman_trace_search tool call. When the optional filters.monitor object is present, BOTH of its keys are mandatory: monitor.id must be a non-blank string and monitor.verdict must be pass|fail|error. This error fires when monitor.id is missing, not a string, or whitespace-only, aborting the call before any /api/v1/traces/search request is issued.","triggerScenarios":"Calling MCP tool caveman_trace_search with filters.monitor that omits id (e.g. { monitor: { verdict: \"pass\" } }), sets monitor.id to a number/null, or passes a blank string like \" \". Unknown extra keys inside monitor throw a different error first, so reaching this line means only id/verdict keys are present and id failed its check.","commonSituations":"An agent assumes verdict alone selects monitor-scoped traces; a partial monitor filter is copied from an earlier trace response; a user trims a larger filter down and drops the id field; monitor id is generated as a number and passed unstringified.","solutions":["Set filters.monitor.id to a non-empty string, e.g. filters: { monitor: { id: \"mon-42\", verdict: \"fail\" } }","Include monitor.verdict too — id alone will throw the sibling 'verdict' error next","Omit filters.monitor entirely when monitor-scoped results are not needed"],"exampleFix":"// before\nconst args = { filters: { monitor: { verdict: \"fail\" } } };\nawait callTool(\"caveman_trace_search\", args);\n\n// after\nconst args = { filters: { monitor: { id: \"mon-42\", verdict: \"fail\" } } };\nawait callTool(\"caveman_trace_search\", args);","handlingStrategy":"validation","validationCode":"const okMonitorId = (f = {}) =>\n  f.monitor === undefined ||\n  (typeof f.monitor?.id === \"string\" && f.monitor.id.trim() !== \"\");\nif (!okMonitorId(args.filters)) throw new Error(\"monitor.id required before calling caveman_trace_search\");","typeGuard":"const isNonEmptyString = (v: unknown): v is string =>\n  typeof v === \"string\" && v.trim() !== \"\";","tryCatchPattern":"const res = await client.callTool({ name: \"caveman_trace_search\", arguments });\nif (res.isError) {\n  const text = res.content?.[0]?.text ?? \"\";\n  if (text.includes(\"monitor.id is required\")) {\n    // drop the incomplete monitor filter and retry once without it\n    delete arguments.filters?.monitor;\n  } else throw new Error(text);\n}","preventionTips":["Treat filters.monitor as an all-or-nothing pair: always set both id and verdict together","Fetch valid monitor ids from the traces/monitor API before filtering on them","Validate args against the tool inputSchema before every callTool"],"tags":["mcp","validation","traces","caveman"],"backgroundTag":"mcp-invalid-params","analyzedSha":"766dce6b1394ebb56a3090748d5a0240a5aefb36","analyzedAt":"2026-08-18T03:14:35.516Z","contentChangedAt":"2026-08-18T03:14:35.516Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}