{"record":{"id":"059e1192b251cc1e","repo":"JuliusBrussee/caveman","slug":"filter-key-must-be-a-boolean","errorCode":null,"errorMessage":"filter ${key} must be a boolean","messagePattern":"filter (.+?) must be a boolean","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/agent-mcp.ts","lineNumber":486,"sourceCode":"  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\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  }","sourceCodeStart":468,"sourceCodeEnd":504,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/agent-mcp.ts#L468-L504","documentation":"Validation error from validateTraceFilters in the agent MCP server: a filter key declared as boolean was supplied with a non-boolean value. The MCP client sent JSON whose type does not match the filter contract for that key.","triggerScenarios":"Passing { has_error: 'true' }, { compressed: 1 }, { has_error: null }, or a query-string-derived \"false\" string.","commonSituations":"URL query params arriving as strings; UIs emitting 0/1; serialization layers coercing booleans; LLM-generated tool args using the string \"true\".","solutions":["Pass real booleans: has_error: true","Convert strings with value === 'true' / 'false' before calling","Omit the key when the flag is unknown rather than passing null"],"exampleFix":"// before\nsearchTraces({ filters: { has_error: 'true' } });\n// after\nsearchTraces({ filters: { has_error: hasError === 'true' } });","handlingStrategy":"type-guard","validationCode":"function assertBooleanFilters(filters) {\n  for (const key of ['has_error', 'compressed']) {\n    const v = filters?.[key];\n    if (v !== undefined && typeof v !== 'boolean') {\n      throw new Error(`filter ${key} must be a boolean`);\n    }\n  }\n}","typeGuard":"function isBoolean(v) {\n  return typeof v === 'boolean';\n}","tryCatchPattern":"try {\n  await searchTraces({ filters });\n} catch (error) {\n  const m = error.message.match(/^filter (\\w+) must be a boolean/);\n  if (m) {\n    if (filters[m[1]] === 'true') filters[m[1]] = true;\n    else if (filters[m[1]] === 'false') filters[m[1]] = false;\n    else delete filters[m[1]];\n  }\n}","preventionTips":["Parse query-string booleans ('true'/'false') into real booleans at the boundary","Avoid 0/1 encodings for these flags in UIs","Omit the key when the value is unknown rather than passing null","Use strict === true / === false comparisons in client code"],"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"}