{"record":{"id":"e93067f0cdff2b79","repo":"JuliusBrussee/caveman","slug":"cursor-sort-value-is-required","errorCode":null,"errorMessage":"cursor.sort_value is required","messagePattern":"cursor\\.sort_value is required","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/agent-mcp.ts","lineNumber":532,"sourceCode":"  optionalEnum(args, \"group_by\", [\"session\", \"workflow\", \"model\", \"member\"]);\n  const pageSize = args.page_size;\n  if (pageSize !== undefined && (!Number.isSafeInteger(pageSize) || (pageSize as number) < 1 || (pageSize as number) > 500)) {\n    throw new Error(\"page_size must be an integer from 1 to 500\");\n  }\n  const sort = args.sort;\n  if (sort !== undefined) {\n    if (!sort || typeof sort !== \"object\" || Array.isArray(sort)) throw new Error(\"sort must be an object\");\n    const unknown = Object.keys(sort).filter((key) => key !== \"by\" && key !== \"dir\");\n    if (unknown.length > 0) throw new Error(`sort has unknown key(s): ${unknown.sort().join(\", \")}`);\n    optionalEnum(sort, \"by\", [\"timestamp\", \"total_cost_usd\", \"latency_ms\", \"total_tokens\"]);\n    optionalEnum(sort, \"dir\", [\"asc\", \"desc\"]);\n  }\n  const cursor = args.cursor;\n  if (cursor !== undefined) {\n    if (!cursor || typeof cursor !== \"object\" || Array.isArray(cursor)) throw new Error(\"cursor must be an object\");\n    const unknown = Object.keys(cursor).filter((key) => key !== \"sort_value\" && key !== \"request_id\" && key !== \"group_key\");\n    if (unknown.length > 0) throw new Error(`cursor has unknown key(s): ${unknown.sort().join(\", \")}`);\n    if (typeof cursor.sort_value !== \"string\" || cursor.sort_value === \"\") throw new Error(\"cursor.sort_value is required\");\n    for (const key of [\"request_id\", \"group_key\"]) {\n      if (cursor[key] !== undefined && typeof cursor[key] !== \"string\") throw new Error(`cursor.${key} must be a string`);\n    }\n  }\n}\n\nasync function callTool(client: AgentMcpClient, name: string, rawArgs: unknown): Promise<ToolResult> {\n  const started = Date.now();\n  let outcome: \"ok\" | \"error\" = \"ok\";\n  try {\n    const args = objectArg(rawArgs);\n    const allowed = TOOL_ARGUMENT_KEYS[name];\n    const unknown = Object.keys(args).filter((key) => !allowed?.has(key));\n    if (unknown.length > 0) throw new Error(`unknown argument(s): ${unknown.sort().join(\", \")}`);\n    let value: JSONValue;\n    switch (name) {\n      case \"caveman_context\": {\n        const [identity, projects, selectedProjectId] = await Promise.all([","sourceCodeStart":514,"sourceCodeEnd":550,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/agent-mcp.ts#L514-L550","documentation":"validateTraceSearch enforces the shape of an optional pagination cursor passed to the trace-search MCP tool. A cursor, if provided, must be an object whose sort_value is a non-empty string; sort_value is the mandatory key the server uses to resume iteration. This error means a cursor object was supplied without a valid sort_value.","triggerScenarios":"Calling the trace search tool with args.cursor set to an object like {} or {sort_value:\"\"} or {sort_value:123} or {request_id:\"r1\"} only (sort_value absent).","commonSituations":"Agents constructing a cursor by hand from a truncated previous response; copying a cursor but dropping its sort_value; JSON round-trips turning sort_value into a non-string; clients caching an empty cursor object after the last page.","solutions":["Set cursor.sort_value to the non-empty string returned by the previous trace-search page","If you have no cursor yet, omit the cursor argument entirely instead of passing {}","Ensure sort_value is a string, not a number or null","Only include the allowed keys sort_value, request_id, group_key"],"exampleFix":"// before\nawait callTool(client, \"caveman_trace_search\", { query: \"err\", cursor: { request_id: \"r1\" } });\n// after\nawait callTool(client, \"caveman_trace_search\", { query: \"err\", cursor: { sort_value: \"2026-09-20T00:00:00Z\", request_id: \"r1\" } });","handlingStrategy":"validation","validationCode":"function isValidCursor(c) { return c === undefined || (typeof c === \"object\" && c !== null && !Array.isArray(c) && typeof c.sort_value === \"string\" && c.sort_value !== \"\"); }","typeGuard":"function isCursor(c): c is { sort_value: string; request_id?: string; group_key?: string } { return typeof c === \"object\" && c !== null && !Array.isArray(c) && typeof (c as any).sort_value === \"string\" && (c as any).sort_value !== \"\"; }","tryCatchPattern":"try { await callTool(client, \"caveman_trace_search\", args); } catch (e) { if (String(e.message).includes(\"cursor.sort_value is required\")) { delete args.cursor; return callTool(client, \"caveman_trace_search\", args); } throw e; }","preventionTips":["Store the full cursor object from the previous page verbatim instead of rebuilding it","Only pass a cursor when resuming pagination; start with no cursor","Never hand-construct cursor objects from partial data","Type the cursor in your client code so sort_value is required"],"tags":["validation","pagination","cursor"],"backgroundTag":"missing-required-argument","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"}