{"record":{"id":"315ca2f59f94d8be","repo":"JuliusBrussee/caveman","slug":"filter-key-must-be-a-finite-number","errorCode":null,"errorMessage":"filter ${key} must be a finite number","messagePattern":"filter (.+?) must be a finite number","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"packages/cli/src/agent-mcp.ts","lineNumber":481,"sourceCode":"    \"member_user_id\",\n    \"api_key_id\",\n    \"optimization_id\",\n    \"status_class\",\n  ];\n  for (const key of stringListKeys) {\n    const value = filters[key];\n    if (value === undefined) continue;\n    if (!Array.isArray(value) || value.length === 0 || value.some((entry) => typeof entry !== \"string\" || entry.trim() === \"\")) {\n      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","sourceCodeStart":463,"sourceCodeEnd":499,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/cli/src/agent-mcp.ts#L463-L499","documentation":"Numeric range filters (min_cost_usd, max_cost_usd, min_total_tokens, max_total_tokens, min_latency_ms, max_latency_ms) must be finite JavaScript numbers when present. Strings (\"0.5\"), NaN, Infinity, null, or booleans throw with the key named.","triggerScenarios":"min_cost_usd: \"0.5\" sent as a string from a form or LLM; NaN produced by a failed parseFloat leaking into args; Infinity from dividing by zero while computing a cap.","commonSituations":"LLM-generated tool calls quoting numbers; client code reading thresholds from env/config as strings without conversion; computed bounds that can be NaN when a denominator is zero.","solutions":["Send raw JSON numbers: min_cost_usd: 0.5, not \"0.5\".","Coerce and guard at the boundary: Number(v) then Number.isFinite check, drop the filter if not.","Default absent — omit the key rather than sending null/NaN."],"exampleFix":"// before\n{ filters: { min_cost_usd: String(threshold) } }\n\n// after\n{ filters: { min_cost_usd: Number(threshold) } } // Number.isFinite checked before send","handlingStrategy":"type-guard","validationCode":"for (const k of [\"min_cost_usd\",\"max_cost_usd\",\"min_total_tokens\",\"max_total_tokens\",\"min_latency_ms\",\"max_latency_ms\"]) {\n  const v = filters[k];\n  if (v !== undefined && (typeof v !== \"number\" || !Number.isFinite(v))) delete filters[k]; // or throw\n}","typeGuard":"function isFiniteNumber(v: unknown): v is number {\n  return typeof v === \"number\" && Number.isFinite(v);\n}","tryCatchPattern":null,"preventionTips":["Number()-convert config/env thresholds at load time and check Number.isFinite.","Never stringify numeric filters in tool args."],"tags":["mcp","validation","filters","numbers"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}