JuliusBrussee/caveman · error

sort must be an object

Error message

sort must be an object

What it means

The optional `sort` argument of trace_search must be a plain object with at most `by` and `dir` keys. If sort is provided but is not an object (number, string, boolean, or null) or is an array, this error is thrown before any key checks.

Solutions

  1. Wrap the sort spec in an object: {"sort":{"by":"timestamp","dir":"desc"}}.
  2. Don't send null — omit the key entirely when not sorting.
  3. Convert array/string sort forms into the object form.
  4. Ensure the object has only `by` and `dir` keys (see the unknown-keys error).

Example fix

// before
{ "sort": "timestamp:desc" }
// after
{ "sort": { "by": "timestamp", "dir": "desc" } }
Defensive patterns

Strategy: type-guard

Validate before calling

if (args.sort !== undefined && (typeof args.sort !== "object" || args.sort === null || Array.isArray(args.sort)))
  throw new Error("sort must be an object like {by, dir}");

Type guard

function isPlainObject(v) {
  return typeof v === "object" && v !== null && !Array.isArray(v);
}

Try / catch

try {
  await traceSearch(args);
} catch (e) {
  if (e.message === "sort must be an object") {
    throw new Error("Use sort: { by: 'timestamp', dir: 'desc' } — string/array sort is not supported");
  }
  throw e;
}

Prevention

When it happens

Trigger: trace_search with {"sort":"timestamp"} (string instead of object), {"sort":["timestamp"]} (array), {"sort":null} (non-undefined null), or {"sort":true}.

Common situations: Shorthand sort strings copied from REST APIs that accept "field:dir"; arrays of sort clauses from Elasticsearch-style clients; JSON configs where sort was a string; null sent explicitly by serializers.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20). Data as JSON: /api/errors/8a35841fa78fa7ba. Report an issue: GitHub.

Appendix: source

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

    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"]);
  optionalEnum(args, "group_by", ["session", "workflow", "model", "member"]);
  const pageSize = args.page_size;
  if (pageSize !== undefined && (!Number.isSafeInteger(pageSize) || (pageSize as number) < 1 || (pageSize as number) > 500)) {
    throw new Error("page_size must be an integer from 1 to 500");
  }
  const sort = args.sort;
  if (sort !== undefined) {
    if (!sort || typeof sort !== "object" || Array.isArray(sort)) throw new Error("sort must be an object");
    const unknown = Object.keys(sort).filter((key) => key !== "by" && key !== "dir");
    if (unknown.length > 0) throw new Error(`sort has unknown key(s): ${unknown.sort().join(", ")}`);
    optionalEnum(sort, "by", ["timestamp", "total_cost_usd", "latency_ms", "total_tokens"]);
    optionalEnum(sort, "dir", ["asc", "desc"]);
  }
  const cursor = args.cursor;
  if (cursor !== undefined) {
    if (!cursor || typeof cursor !== "object" || Array.isArray(cursor)) throw new Error("cursor must be an object");
    const unknown = Object.keys(cursor).filter((key) => key !== "sort_value" && key !== "request_id" && key !== "group_key");
    if (unknown.length > 0) throw new Error(`cursor has unknown key(s): ${unknown.sort().join(", ")}`);
    if (typeof cursor.sort_value !== "string" || cursor.sort_value === "") throw new Error("cursor.sort_value is required");
    for (const key of ["request_id", "group_key"]) {
      if (cursor[key] !== undefined && typeof cursor[key] !== "string") throw new Error(`cursor.${key} must be a string`);
    }
  }
}

async function callTool(client: AgentMcpClient, name: string, rawArgs: unknown): Promise<ToolResult> {

View on GitHub (pinned to 3ee70a1026)