moeru-ai/airi · error · TypeError

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

Error message

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

What it means

Thrown as a TypeError by serializeToolParameters() when a tool definition's inputSchema is neither a Standard Schema instance (detected by the `~standard` marker) nor a JSON Schema object (a plain object containing `type`, `properties`, `$schema`, or `$ref`). The serializer cannot convert the value into the host-safe HostDataRecord shape, so registration aborts.

Source

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

/**
 * 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 27111382b4)

Solutions

  1. Pass a Valibot (or other xsschema-compatible) schema object directly as inputSchema so the `~standard` marker is present.
  2. Or pass a JSON Schema root object that includes at least one of `type`, `properties`, `$schema`, or `$ref`.
  3. If using a wrapped/custom schema object, unwrap it so the raw schema instance reaches registerTool.
  4. Verify the schema library version supports Standard Schema (`~standard` property) before relying on isStandardSchema detection.

Example fix

// before
await tools.registerTool({
  id: 'search',
  title: 'Search',
  description: 'Search items',
  inputSchema: { description: 'search params' }, // no type/properties/$schema/$ref
  execute,
})

// after (JSON Schema)
await tools.registerTool({
  id: 'search',
  title: 'Search',
  description: 'Search items',
  inputSchema: { type: 'object', properties: { query: { type: 'string' } } },
  execute,
})

// after (Valibot Standard Schema)
import * as v from 'valibot'
await tools.registerTool({
  id: 'search',
  title: 'Search',
  description: 'Search items',
  inputSchema: v.object({ query: v.string() }),
  execute,
})
Defensive patterns

Strategy: type-guard

Validate before calling

function isValidToolInputSchema(value: unknown): boolean {
  if (value && typeof value === 'object' && '~standard' in value) return true
  if (value && typeof value === 'object' && !Array.isArray(value)
      && ('type' in value || 'properties' in value || '$schema' in value || '$ref' in value)) return true
  return false
}
if (!isValidToolInputSchema(definition.inputSchema)) {
  throw new TypeError('inputSchema must be a JSON Schema object or Standard Schema instance')
}

Type guard

import type { JsonSchema, Schema as StandardSchemaV1 } from 'xsschema'

function isStandardSchema(v: unknown): v is StandardSchemaV1 {
  return Boolean(v && typeof v === 'object' && '~standard' in v)
}

function isJsonSchemaRecord(v: unknown): v is JsonSchema {
  return Boolean(v && typeof v === 'object' && !Array.isArray(v)
    && ('type' in v || 'properties' in v || '$schema' in v || '$ref' in v))
}

Try / catch

try {
  await tools.registerTool(definition)
} catch (error) {
  if (error instanceof TypeError && /Tool input schema/.test(error.message)) {
    // inputSchema was neither Standard Schema nor JSON Schema; fix the definition
  } else {
    throw error
  }
}

Prevention

When it happens

Trigger: Calling toolKit.registerTool() with a definition whose inputSchema is undefined, null, a string, a number, an array, or a plain object lacking all of `type`/`properties`/`$schema`/`$ref`. Passing a raw class instance that is not a recognized schema library object also triggers it.

Common situations: Author forgets the inputSchema field entirely, passes a Zod/Valibot schema from a version that does not implement the Standard Schema spec, passes a JSON Schema fragment that is only a sub-schema (e.g. just `{ properties: ... }` works, but `{ description: '...' }` alone does not), or passes a wrapped object like `{ schema: ... }` instead of the schema itself.

Related errors


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