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

  1. Remove every key from filter.monitor except `id` and `verdict`.
  2. Fix typos so keys are exactly `id` and `verdict`.
  3. Move extra data (notes, labels) out of the filter into another argument or tool field.
  4. 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

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


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)