ComposioHQ/composio · error · Error

experimental_subAgent() requires Zod 4 with z.toJSONSchema()

Error message

experimental_subAgent() requires Zod 4 with z.toJSONSchema() when using options.schema.

What it means

When you pass a Zod schema via options.schema, the CLI must convert it to JSON Schema using z.toJSONSchema(), which only exists in Zod 4. If the installed Zod runtime lacks z.toJSONSchema, this error is thrown during option normalization.

Source

Thrown at ts/packages/cli/src/services/run-helpers-runtime.ts:370

  }
  const requestedTarget = options.target;
  if (
    requestedTarget !== undefined &&
    requestedTarget !== 'claude' &&
    requestedTarget !== 'codex' &&
    requestedTarget !== 'user'
  ) {
    throw new Error(
      'experimental_subAgent() target must be "claude", "codex", or "user" when provided.'
    );
  }
  const inputSchema = options.schema ?? options.jsonSchema;
  let structuredSchema: Record<string, unknown> | undefined;
  let zodSchema: z.ZodType | undefined;
  if (inputSchema !== undefined) {
    if (inputSchema instanceof z.ZodType) {
      if (typeof z.toJSONSchema !== 'function') {
        throw new Error(
          'experimental_subAgent() requires Zod 4 with z.toJSONSchema() when using options.schema.'
        );
      }
      zodSchema = inputSchema;
      const generatedSchema = z.toJSONSchema(inputSchema);
      structuredSchema = Schema.decodeUnknownSync(JsonObject)(generatedSchema);
    } else if (Predicate.isRecord(inputSchema)) {
      structuredSchema = inputSchema;
    } else {
      throw new Error('experimental_subAgent() schema must be a Zod schema or JSON Schema object.');
    }
  }
  return {
    ...(requestedTarget === undefined ? {} : { target: requestedTarget }),
    ...(typeof options.model === 'string' ? { model: options.model } : {}),
    ...(options.schema === undefined ? {} : { schema: options.schema }),
    ...(options.jsonSchema === undefined ? {} : { jsonSchema: options.jsonSchema }),
    ...(structuredSchema === undefined ? {} : { structuredSchema }),

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Upgrade zod to v4 (pnpm add zod@^4) so z.toJSONSchema exists
  2. Alternatively pass options.jsonSchema with a plain JSON Schema object instead of a Zod schema
  3. Check for duplicate zod installs (pnpm why zod) and dedupe so the schema instance and the CLI share Zod 4

Example fix

// before
import { z } from 'zod'; // zod@3
await experimental_subAgent('p', { schema: z.object({ n: z.number() }) });
// after
pnpm add zod@^4
// or bypass Zod:
await experimental_subAgent('p', { jsonSchema: { type: 'object', properties: { n: { type: 'number' } } } });
Defensive patterns

Strategy: validation

Validate before calling

import { z } from 'zod';
if (opts.schema instanceof z.ZodType && typeof z.toJSONSchema !== 'function') {
  throw new Error('Zod 4 required; use options.jsonSchema instead');
}

Type guard

const hasToJsonSchema = (zz: typeof z): zz is typeof z & { toJSONSchema: (s: z.ZodType) => unknown } => typeof zz.toJSONSchema === 'function';

Try / catch

catch (e) { if (e instanceof Error && e.message.includes('Zod 4')) { /* fall back to jsonSchema option */ } }

Prevention

When it happens

Trigger: Calling experimental_subAgent(prompt, { schema: z.object({...}) }) while the resolved zod package in the CLI runtime is Zod 3 (which has no toJSONSchema).

Common situations: A project pinned to zod@^3, or a duplicate zod install where the schema instance comes from Zod 3 while the CLI expects Zod 4; upgrading the CLI but not zod, or vice versa.

Related errors


AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28). Data as JSON: /api/errors/7bfcd96076eb6c74. Report an issue: GitHub.