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
- Pass a plain JSON Schema object: inputSchema: { type: 'object', properties: { query: { type: 'string' } }, required: ['query'] }.
- 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).
- If you are on an older Zod, either upgrade or call zod-to-json-schema yourself and pass the resulting plain object.
- 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
- Standardize on Zod >= 3.24 or Valibot v1 so schemas carry the '~standard' property the SDK detects.
- Never JSON.stringify schemas before passing them; pass the object itself.
- Lint extension code for registerTool calls missing inputSchema — tools without parameters still need { type: 'object', properties: {} }.
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
- Tamagotchi extension tool not found
- toolKit requires a host tool registry runtime.
- Extension entrypoint id
- gameletKit requires a host binding runtime.
- gameletKit requires a host gamelet orchestration runtime.
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)