JuliusBrussee/caveman · warning

filter monitor.verdict must be pass, fail, or error

Error message

filter monitor.verdict must be pass, fail, or error

What it means

monitor.verdict, when provided as a string, must be exactly one of "pass", "fail", or "error". Any other value ("passed", "ok", "failed", "success") is rejected — note the past-tense forms are not accepted.

Source

Thrown at packages/cli/src/agent-mcp.ts:495

  }
  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];
    if (value !== undefined && typeof value !== "string") throw new Error(`${key} must be a string`);
  }
  optionalEnum(args, "date_field", ["occurred", "updated"]);

View on GitHub (pinned to 27d5a3981a)

Solutions

  1. Use the exact tokens: "pass", "fail", "error".
  2. Omit verdict to match all verdicts for a monitor id.
  3. Normalize your app's verdict vocabulary to these three values at the boundary.

Example fix

// before
{ filters: { monitor: { id: "mon_1", verdict: "passed" } } }

// after
{ filters: { monitor: { id: "mon_1", verdict: "pass" } } }
Defensive patterns

Strategy: validation

Validate before calling

if (monitor.verdict !== undefined && !["pass", "fail", "error"].includes(monitor.verdict)) {
  throw new Error("verdict must be pass|fail|error");
}

Type guard

function isMonitorVerdict(v: unknown): v is "pass" | "fail" | "error" {
  return v === "pass" || v === "fail" || v === "error";
}

Prevention

When it happens

Trigger: monitor: { id: "mon_1", verdict: "passed" }; using UI labels like "Success"/"OK" instead of the wire tokens.

Common situations: Translating display labels into filters; a model inflecting the token ("failed" instead of "fail").

Related errors


AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15). Data as JSON: /api/errors/9dc9d92d3cf7e227. Report an issue: GitHub.