{"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":"error","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/3ee70a102609e550bd2e68004bf5990a9341c851/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 numbers when present. Non-numbers, NaN, and ±Infinity throw `filter ${key} must be a finite number`.","triggerScenarios":"Passing { min_cost_usd: \"0.5\" } (string), { max_latency_ms: Infinity }, { min_total_tokens: NaN }, or null for any of these keys; JSON has no Infinity so it may arrive as null or a string after serialization.","commonSituations":"String values from form inputs never converted to numbers; NaN from failed parseFloat; Infinity used as \"no upper bound\" instead of omitting the key; null from JSON.stringify of Infinity.","solutions":["Pass numbers, not strings: min_cost_usd: 0.5","Omit the key when you mean \"unbounded\" instead of using Infinity","Guard with Number.isFinite before sending; drop or fix non-finite values","Convert form/query-string input with Number() and validate"],"exampleFix":"// before\nsearchTraces({ filters: { min_cost_usd: '0.5', max_latency_ms: Infinity } });\n// after\nsearchTraces({ filters: { min_cost_usd: 0.5 } }); // omit max_latency_ms for unbounded","handlingStrategy":"validation","validationCode":"const NUMERIC_FILTERS = ['min_cost_usd','max_cost_usd','min_total_tokens','max_total_tokens','min_latency_ms','max_latency_ms'];\nfunction assertFiniteNumbers(filters) {\n  for (const key of NUMERIC_FILTERS) {\n    const v = filters?.[key];\n    if (v !== undefined && (typeof v !== 'number' || !Number.isFinite(v))) {\n      throw new Error(`filter ${key} must be a finite number`);\n    }\n  }\n}","typeGuard":"function isFiniteNumber(v) {\n  return typeof v === 'number' && Number.isFinite(v);\n}","tryCatchPattern":"try {\n  await searchTraces({ filters });\n} catch (error) {\n  const m = error.message.match(/^filter (\\w+) must be a finite number/);\n  if (m) {\n    const n = Number(filters[m[1]]);\n    if (Number.isFinite(n)) filters[m[1]] = n; else delete filters[m[1]];\n  }\n}","preventionTips":["Convert form/query-string input with Number() before sending","Omit range keys for unbounded sides instead of using Infinity","Guard with Number.isFinite; drop or fix NaN/null values","Type numeric filters as number | undefined in client code"],"tags":["mcp","validation","traces","numeric"],"backgroundTag":"invalid-argument-value","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"}