ComposioHQ/composio · error · Error

experimental_subAgent() schema must be a Zod schema or JSON

Error message

experimental_subAgent() schema must be a Zod schema or JSON Schema object.

What it means

The schema option for experimental_subAgent() accepts only two shapes: a Zod schema instance (inputSchema instanceof z.ZodType) or a plain record (JSON Schema object). Anything else — a number, function, class instance, array — triggers this error in normalizeInvokeAgentOptions.

Source

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

    );
  }
  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 }),
    ...(zodSchema === undefined ? {} : { zodSchema }),
  };
};

const normalizeProxyToolkit = (toolkit: string) => {
  if (typeof toolkit !== 'string' || toolkit.trim().length === 0) {
    throw new Error('proxy() requires a non-empty toolkit string.');
  }
  return toolkit.trim();
};

View on GitHub (pinned to 64b1b85502)

Solutions

  1. Pass a plain JSON Schema object via options.jsonSchema (or options.schema as a record)
  2. If using Zod, ensure only one zod copy is installed (pnpm dedupe / pnpm why zod) so instanceof works
  3. Do not pass serialized schema strings; parse them to objects first

Example fix

// before
await experimental_subAgent('p', { schema: '{"type":"object"}' });
// after
await experimental_subAgent('p', { jsonSchema: JSON.parse(schemaString) });
Defensive patterns

Strategy: type-guard

Validate before calling

const isPlainObject = (v: unknown): v is Record<string, unknown> => typeof v === 'object' && v !== null && !Array.isArray(v);
if (opts.schema !== undefined && !(opts.schema instanceof z.ZodType) && !isPlainObject(opts.schema)) throw new TypeError('schema must be Zod or JSON Schema object');

Type guard

const isZodOrJsonSchema = (v: unknown): v is z.ZodType | Record<string, unknown> => v instanceof z.ZodType || (Predicate.isRecord?.(v) ?? (typeof v === 'object' && v !== null && !Array.isArray(v)));

Prevention

When it happens

Trigger: Passing options.schema as a JSON string, an array, a Yup/Valibot/Effect schema, or a Zod schema from a different duplicated zod package instance (instanceof check fails across copies).

Common situations: JSON.stringify'd schema stored in a variable, using another validation library, or two copies of zod in node_modules making the instanceof check fail even though the object is a Zod schema.

Related errors


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