Skip to content

fix(ai/gateway): wrap tool inputSchema with ai SDK's jsonSchema() helper#1748

Closed
michaeladair44 wants to merge 1 commit into
garrytan:masterfrom
michaeladair44:fix/tool-input-schema-jsonschema-wrapper
Closed

fix(ai/gateway): wrap tool inputSchema with ai SDK's jsonSchema() helper#1748
michaeladair44 wants to merge 1 commit into
garrytan:masterfrom
michaeladair44:fix/tool-input-schema-jsonschema-wrapper

Conversation

@michaeladair44

Copy link
Copy Markdown

Symptom

A subagent fan-out we run on top of gbrain (one parent → 38 children, each calling chat() with tools defined) died — all 38 children — with the same runtime error before a single LLM call could fire:

[chat(anthropic:claude-haiku-4-5-20251001)] schema is not a function. (In 'schema()', 'schema' is an instance of Object)

Root cause

src/core/ai/gateway.ts builds the tools map for the Vercel AI SDK like this:

const tools = (opts.tools ?? []).reduce((acc, t) => {
  acc[t.name] = {
    description: t.description,
    inputSchema: { jsonSchema: t.inputSchema } as any,
  };
  return acc;
}, {} as Record<string, any>);

The bare { jsonSchema: t.inputSchema } as any is then handed to AI SDK v6's asSchema() in @ai-sdk/provider-utils/dist/index.mjs (~line 2126). asSchema() branches on what kind of input it received:

  • a real Schema (has [schemaSymbol] + validate)
  • a ZodSchema
  • a StandardSchema (~standard)
  • a LazySchema (function)

A plain { jsonSchema: ... } object matches none of these branches, so the SDK falls through to invoking the input as a function: schema()schema is not a function because schema is an Object literal.

Fix

ai already exports the jsonSchema() helper (re-exported from @ai-sdk/provider-utils) precisely for this case — it wraps a JSON Schema 7 object in a proper Schema<T> with [schemaSymbol] and a no-op validate. Two-line change:

- import { embed as aiEmbed, embedMany, generateObject, generateText } from 'ai';
+ import { embed as aiEmbed, embedMany, generateObject, generateText, jsonSchema } from 'ai';
...
- inputSchema: { jsonSchema: t.inputSchema } as any,
+ inputSchema: jsonSchema(t.inputSchema as any),

Verification

  • bun build --compile --outfile bin/gbrain src/cli.ts — clean.
  • Hot-restarted gbrain autopilot against the patched source.
  • Retried one of the dead subagent children (job 13028, model anthropic:claude-haiku-4-5-20251001, tools enabled) — the SDK now accepts the tool schema and the LLM call fires.

Impact

Any caller of chat() that passes opts.tools will hit this. Hot path for tool-using minions / subagent fan-outs.

Scope

One file, two lines. No new dependencies — jsonSchema is already exported from the ai package this repo pins (^6.0.168).

The bare `{ jsonSchema: t.inputSchema } as any` object was passed
directly to the Vercel AI SDK v6's tool definition. AI SDK v6's
`asSchema()` (provider-utils/dist/index.mjs:2126) routes a `Schema` (with
`[schemaSymbol]` + `validate`), a `ZodSchema`, a `StandardSchema`
(`~standard`), or a `LazySchema` (function). A bare `{ jsonSchema }`
object matches none of these, so the SDK falls through to invoking the
input as a function, throwing:

  schema is not a function. (In 'schema()', 'schema' is an instance of Object)

Use the SDK's own `jsonSchema(...)` helper, which produces a proper
`Schema<T>` and is the documented public API for this case.

Surfaced when a gbrain agent fan-out (38 children) all died on the same
runtime error before any LLM call could fire. Refs ADA-495.
@garrytan

garrytan commented Jun 3, 2026

Copy link
Copy Markdown
Owner

Landed in #1809 (v0.42.19.0). Your jsonSchema() primary fix is exactly right and is in the shipped change — thank you. We extended it with the v6 tool-result-shape fix (#1764's secondary failure) so the agent loop works end to end on non-Anthropic providers, and added a real-SDK regression test. Crediting you as Co-Author. Closing in favor of the combined wave.

@garrytan garrytan closed this Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants