moeru-ai/airi · error · TypeError

Tool input schema must be a JSON Schema object or a…

Error message

Tool input schema must be a JSON Schema object or a Standard Schema instance.

What it means

toolKit.registerTool serializes definition.inputSchema for host transport via serializeToolParameters (packages/plugin-sdk-tamagotchi/src/tools/index.ts). It accepts exactly two shapes: a Standard Schema v1 instance (detected via the '~standard' property — Zod 3.24+, Valibot v1) or a plain JSON Schema record (non-null object). Anything else — undefined, a string, an array, a class instance without '~standard', or a Zod version too old to implement Standard Schema — hits the final TypeError. The value is then normalized strictly (normalizeStrictToolParameterSchema) and stored as a HostDataRecord in the host tool registry.

Solutions

  1. Pass a plain JSON Schema object: inputSchema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] }.
  2. Or pass a Standard Schema library instance: Zod >= 3.24 (z.object({...})) or Valibot v1 — verify the installed version actually implements Standard Schema ('~standard' in schema).
  3. If you are on an older Zod, either upgrade or call zod-to-json-schema yourself and pass the resulting plain object.
  4. For tools with no parameters, pass an explicit empty object schema { type: 'object', properties: {} } rather than undefined.

Example fix

// before
await tools.registerTool({
  id: 'weather',
  inputSchema: JSON.stringify({ type: 'object' }), // string: throws
})

// after
await tools.registerTool({
  id: 'weather',
  inputSchema: z.object({ city: z.string() }), // Zod >= 3.24 Standard Schema
})
Defensive patterns

Strategy: type-guard

Validate before calling

function toValidInputSchema(input: unknown): object {
  if (isStandardSchemaLike(input) || isPlainObjectSchema(input)) return input as object
  return { type: 'object', properties: {} } // safe empty schema for no-arg tools
}
await tools.registerTool({ id, title, inputSchema: toValidInputSchema(raw) })

Type guard

function isPlainObjectSchema(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v) && !(v instanceof Date)
}
function isStandardSchemaLike(v: unknown): v is { '~standard': { version: 1 } } {
  return typeof v === 'object' && v !== null
    && typeof (v as { '~standard'?: { version?: unknown } })['~standard']?.version === 'number'
}

Try / catch

try {
  await tools.registerTool(definition)
} catch (error) {
  if (error instanceof TypeError && errorMessageFrom(error).includes('Tool input schema')) {
    // fall back to an explicit JSON Schema and register again
    await tools.registerTool({ ...definition, inputSchema: { type: 'object', properties: {} } })
  } else throw error
}

Prevention

When it happens

Trigger: Calling registerTool({ inputSchema: ... }) with: no inputSchema field at all; a Zod schema from Zod < 3.24 (no standard-schema support); a JSON Schema passed as a JSON string; a GraphQL/typebox artifact that is neither a plain object nor Standard Schema; null.

Common situations: Dual-Zod setups where the extension resolves an older zod than the SDK expects; migrating from hand-written JSON Schema objects to Zod (or vice versa) and leaving one call site with a stringified schema; forgetting inputSchema for a tool that takes no arguments (use { type: 'object', properties: {} } instead of omitting).

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of moeru-ai/airi@677329427f (2026-08-18). Data as JSON: /api/errors/70a7a2e715cb1c58. Report an issue: GitHub.

Appendix: source

Thrown at packages/plugin-sdk-tamagotchi/src/tools/index.ts:269

/**
 * Normalizes tool parameter schemas into the host-safe record shape expected by plugin-sdk.
 *
 * Before:
 * - A Standard Schema instance or a JSON Schema-like authoring object
 *
 * After:
 * - A validated `HostDataRecord` safe to store in the host tool registry
 */
async function serializeToolParameters(inputSchema: unknown): Promise<HostDataRecord> {
  if (isStandardSchema(inputSchema)) {
    return toHostDataRecord(normalizeStrictToolParameterSchema(await toJsonSchema(inputSchema)))
  }

  if (isJsonSchemaRecord(inputSchema)) {
    return toHostDataRecord(normalizeStrictToolParameterSchema(structuredClone(inputSchema)))
  }

  throw new TypeError('Tool input schema must be a JSON Schema object or a Standard Schema instance.')
}

/**
 * Exposes tamagotchi tool registration as a module-scoped extension kit.
 *
 * Use when:
 * - An extension module wants to register tools through `module.kits.use(toolKit)`
 * - The host should keep tool transport, permission, and binding details outside authoring code
 *
 * Expects:
 * - The host provides tool registry APIs when creating the kit client
 *
 * Returns:
 * - A client that registers LLM tools without depending on domain-specific kits
 */
export const toolKit = defineKit<ToolKitClient>({
  id: 'kit.tool',
  version: '1.0.0',

View on GitHub (pinned to 677329427f)