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

  1. 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
  2. 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
  3. 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

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


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