JuliusBrussee/caveman · error
must be a string
Error message
${key} must be a string What it means
In validateTraceSearch, the optional range bounds `from` and `to` must be strings (date/timestamp strings). Passing numbers, objects, arrays, or booleans for either throws this error naming the offending key.
Solutions
- Convert dates to strings before calling, e.g. new Date(x).toISOString() for from/to.
- Use ISO 8601 formatted strings ("2026-01-01T00:00:00Z").
- Remove from/to if unbounded queries are acceptable (both optional).
- Check the API's expected date format documented for the tool.
Example fix
// before
{ "from": 1735689600000, "to": 1738368000000 }
// after
{ "from": "2025-01-01T00:00:00Z", "to": "2025-02-01T00:00:00Z" } Defensive patterns
Strategy: type-guard
Validate before calling
for (const k of ["from", "to"]) {
if (args[k] !== undefined && typeof args[k] !== "string")
args[k] = new Date(args[k]).toISOString();
} Type guard
function isDateString(v) {
return typeof v === "string" && !Number.isNaN(Date.parse(v));
} Try / catch
try {
await traceSearch(args);
} catch (e) {
if (/^(from|to) must be a string$/.test(e.message)) {
const key = e.message.split(" ")[0];
args[key] = new Date(args[key]).toISOString();
return traceSearch(args);
}
throw e;
} Prevention
- Serialize all dates to ISO strings at the client boundary.
- Never pass Date objects or epoch numbers into JSON tool arguments.
- Add a pre-send serializer for date-like fields.
When it happens
Trigger: trace_search with {"from":1700000000000} (epoch number instead of ISO string), {"to":{"$date":"..."}} (object), or {"from":true} — any defined non-string value for from/to.
Common situations: Passing JS Date objects or epoch millis from client code that forgot to serialize; JSON configs where dates were numbers; mixing this API's string-date convention with another API that accepts numbers.
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
- cursor must be an object
- sort must be an object
- cursor has unknown key(s)
- cursor. must be a string
- is not an array (got )
AI-assisted analysis of JuliusBrussee/caveman@3ee70a1026 (2026-09-20).
Data as JSON: /api/errors/2c3dc1701ee7c69b.
Report an issue: GitHub.
Appendix: source
Thrown at packages/cli/src/agent-mcp.ts:511
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"]);
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");View on GitHub (pinned to 3ee70a1026)