JuliusBrussee/caveman · error
filter monitor has unknown key(s)
Error message
filter monitor has unknown key(s): ${monitorUnknown.sort().join(", ")} What it means
The trace_search tool's validateTraceFilters rejects a `monitor` filter object containing keys other than `id` and `verdict`. The library whitelists exactly these two sub-fields to keep the filter schema strict, so any extra key (typo, renamed field, or nested object) produces this error listing the offending keys sorted alphabetically.
Solutions
- Remove every key from filter.monitor except `id` and `verdict`.
- Fix typos so keys are exactly `id` and `verdict`.
- Move extra data (notes, labels) out of the filter into another argument or tool field.
- Check the tool's input schema for the currently allowed monitor keys.
Example fix
// before
{ "filter": { "monitor": { "id": "m_1", "verdict": "pass", "reason": "ok" } } }
// after
{ "filter": { "monitor": { "id": "m_1", "verdict": "pass" } } } Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED = new Set(["id", "verdict"]);
if (filter.monitor && Object.keys(filter.monitor).some(k => !ALLOWED.has(k)))
throw new Error("filter.monitor may only contain id and verdict"); Type guard
function isValidMonitorFilter(m) {
return typeof m === "object" && m !== null && !Array.isArray(m) &&
Object.keys(m).every(k => k === "id" || k === "verdict");
} Try / catch
try {
await traceSearch(args);
} catch (e) {
if (String(e.message).startsWith("filter monitor has unknown key")) {
args.filter.monitor = { id: args.filter.monitor.id, verdict: args.filter.monitor.verdict };
return traceSearch(args);
}
throw e;
} Prevention
- Define the monitor filter as a typed object with only id/verdict fields.
- Build filters via a factory function instead of ad-hoc object literals.
- Validate against the tool's JSON schema before each call.
When it happens
Trigger: Calling the trace_search MCP tool with arguments like {"filter":{"monitor":{"id":"m_1","verdict":"pass","note":"x"}}} — any key in filter.monitor besides id/verdict, including null-valued extras or typos like "verdicts" or "monitorId".
Common situations: Copying a filter shape from a different endpoint that allows more monitor fields; adding UI metadata (label, comment) into the filter object; version drift where a newer API surface accepts more keys than this CLI's validator.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- cursor has unknown key(s)
- filter monitor.id is required
- filter monitor.verdict must be pass, fail, or error
- pending transaction is malformed; refusing recovery
- sort has unknown key(s)
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/6bb1121922d4b4ad.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/agent-mcp.ts:492
if (key === "status_class" && value.some((entry) => !["2xx", "4xx", "5xx"].includes(entry as string))) {
throw new Error("filter status_class values must be 2xx, 4xx, or 5xx");
}
}
for (const key of ["min_cost_usd", "max_cost_usd", "min_total_tokens", "max_total_tokens", "min_latency_ms", "max_latency_ms"]) {
const value = filters[key];
if (value !== undefined && (typeof value !== "number" || !Number.isFinite(value))) {
throw new Error(`filter ${key} must be a finite number`);
}
}
for (const key of ["has_error", "compressed"]) {
const value = filters[key];
if (value !== undefined && typeof value !== "boolean") throw new Error(`filter ${key} must be a boolean`);
}
const monitor = filters.monitor;
if (monitor !== undefined) {
if (!monitor || typeof monitor !== "object" || Array.isArray(monitor)) throw new Error("filter monitor must be an object");
const monitorUnknown = Object.keys(monitor).filter((key) => key !== "id" && key !== "verdict");
if (monitorUnknown.length > 0) throw new Error(`filter monitor has unknown key(s): ${monitorUnknown.sort().join(", ")}`);
if (typeof monitor.id !== "string" || monitor.id.trim() === "") throw new Error("filter monitor.id is required");
if (typeof monitor.verdict !== "string" || !["pass", "fail", "error"].includes(monitor.verdict)) {
throw new Error("filter monitor.verdict must be pass, fail, or error");
}
}
}
function optionalEnum(args: JSONObject, key: string, values: string[]): void {
const value = args[key];
if (value !== undefined && (typeof value !== "string" || !values.includes(value))) {
throw new Error(`${key} must be one of ${values.join(", ")}`);
}
}
function validateTraceSearch(args: JSONObject): void {
validateTraceFilters(args);
for (const key of ["from", "to"]) {
const value = args[key];View on GitHub (pinned to 3ee70a1026)