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
- Pass a plain JSON Schema object via options.jsonSchema (or options.schema as a record)
- If using Zod, ensure only one zod copy is installed (pnpm dedupe / pnpm why zod) so instanceof works
- 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
- Parse JSON strings to objects before passing
- Never mix validation libraries into options.schema
- Dedupe zod so instanceof checks hold
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
- experimental_subAgent() requires Zod 4 with z.toJSONSchema()
- experimental_subAgent() structured output failed schema vali
- experimental_subAgent() target must be "claude", "codex", or
- experimental_subAgent() requires a non-empty prompt string.
- Invalid arguments for local tool ${resolution.finalSlug}: ${
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/7f9999d15360f8ed.
Report an issue: GitHub.