JuliusBrussee/caveman · error · Error
caveman agent: tool Standard JSON Schema converter is invali
Error message
caveman agent: tool Standard JSON Schema converter is invalid
What it means
Thrown by the tool() builder's standardToolSchema() probe when a tool schema advertises a Standard Schema '~standard' props object whose optional jsonSchema converter is malformed. The framework uses that converter (StandardJSONSchemaV1.Converter, which must expose an input() function) to lower the schema to JSON Schema for the provider; if jsonSchema exists but is not an object with a callable input property, the tool definition is rejected at construction time.
Source
Thrown at packages/agent/src/primitives.ts:221
return Object.freeze(definition);
}
function standardToolSchema(
value: unknown,
): StandardSchemaV1.Props<unknown, unknown> & {
jsonSchema?: StandardJSONSchemaV1.Converter;
} | undefined {
if (!isRecord(value) || !isRecord(value["~standard"])) return undefined;
const standard = value["~standard"];
if (standard.version !== 1 || typeof standard.vendor !== "string" ||
typeof standard.validate !== "function") {
throw new Error(
"caveman agent: tool Standard Schema must implement version 1 validation",
);
}
if (standard.jsonSchema !== undefined &&
(!isRecord(standard.jsonSchema) || typeof standard.jsonSchema.input !== "function")) {
throw new Error("caveman agent: tool Standard JSON Schema converter is invalid");
}
return standard as unknown as StandardSchemaV1.Props<unknown, unknown> & {
jsonSchema?: StandardJSONSchemaV1.Converter;
};
}
function isRecord(value: unknown): value is Record<string, unknown> {
return value !== null && typeof value === "object" && !Array.isArray(value);
}
function artifactResultPolicy(definition: ArtifactDefinition): ToolResultPolicy {
if (definition.strategy === "verbatim") {
return definition.recovery === "exact_ccr" ? "exact_ccr" : "inline";
}
if (definition.strategy === "json-index") return "compress";
return "page";
}
View on GitHub (pinned to 27d5a3981a)
Solutions
- Make standard.jsonSchema an object with an input() function: { input: (schema) => jsonSchemaObject } — or omit jsonSchema entirely so the framework falls back to its own conversion
- If using Zod/Valibot/ArkType/TypeBox through their official Standard Schema support, pass the library's own exported schema instead of a rewrapped props object
- If you do not need the Standard Schema path, pass a TypeBox TSchema (the framework's native schema type) as the tool's input schema
Example fix
// before
const schema = {
'~standard': {
version: 1,
vendor: 'my-lib',
validate: (v) => ({ value: v }),
jsonSchema: { type: 'object' }, // wrong: plain schema, not a converter
},
};
tool({ name: 't', schema, execute: async () => {} });
// after
const schema = {
'~standard': {
version: 1,
vendor: 'my-lib',
validate: (v) => ({ value: v }),
jsonSchema: { input: () => ({ type: 'object' }) }, // converter with input()
},
};
tool({ name: 't', schema, execute: async () => {} }); Defensive patterns
Strategy: type-guard
Validate before calling
function hasValidStandardJsonSchema(schema: unknown): boolean {
const props = (schema as { '~standard'?: unknown })?.['~standard'];
if (props === null || typeof props !== 'object') return true; // no standard path, fine
const js = (props as { jsonSchema?: unknown }).jsonSchema;
if (js === undefined) return true;
return typeof js === 'object' && js !== null &&
typeof (js as { input?: unknown }).input === 'function';
} Type guard
function isValidStandardToolSchema(value: unknown): value is { '~standard': { version: 1; vendor: string; validate: () => unknown; jsonSchema?: { input: (s: unknown) => unknown } } } {
if (typeof value !== 'object' || value === null) return false;
const s = (value as Record<string, unknown>)['~standard'];
if (typeof s !== 'object' || s === null) return false;
const p = s as Record<string, unknown>;
if (p.version !== 1 || typeof p.vendor !== 'string' || typeof p.validate !== 'function') return false;
const js = p.jsonSchema;
if (js !== undefined && (typeof js !== 'object' || js === null || typeof (js as Record<string, unknown>).input !== 'function')) return false;
return true;
} Try / catch
try { tool({ name, schema, execute }); } catch (e) { if (e instanceof Error && e.message.includes('Standard JSON Schema converter')) throw new ConfigError(`tool ${name}: fix or drop schema['~standard'].jsonSchema`, { cause: e }); throw e; } Prevention
- Prefer library-native Standard Schema exports (Zod/Valibot/ArkType) over hand-rolled '~standard' objects
- Treat jsonSchema as optional: omit it unless you specifically need custom JSON Schema lowering
- Unit-test every custom schema wrapper against the tool() builder
When it happens
Trigger: Calling tool({...}) with a schema object that has a '~standard' props bag where standard.jsonSchema is set to a non-record (e.g. a string or array) or is a record whose input is not a function. Typically a hand-rolled Standard Schema implementation or a wrapper library that attaches a partial jsonSchema object.
Common situations: Writing a custom Standard Schema v1 wrapper for a tool and stubbing jsonSchema as a plain schema object instead of a converter; upgrading a schema library that changed the jsonSchema converter shape; copying the StandardSchemaV1.Props interface but forgetting the converter's method form.
Related errors
- cave_live_eval_sandbox_profile_invalid
- cave_tool_definition_invalid
- cave_duplicate_tool_name
- caveman agent: Standard Schema emitted invalid input JSON Sc
- caveman agent: tool Standard Schema must implement version 1
AI-assisted analysis of JuliusBrussee/caveman@27d5a3981a (2026-08-15).
Data as JSON: /api/errors/4d24fdfbfe373f73.
Report an issue: GitHub.