ComposioHQ/composio · error · Error
experimental_subAgent() target must be "claude", "codex", or
Error message
experimental_subAgent() target must be "claude", "codex", or "user" when provided.
What it means
experimental_subAgent() accepts an optional options.target that must be one of the literal strings "claude", "codex", or "user". normalizeInvokeAgentOptions throws this error when target is provided but is not one of those three exact strings. It is a developer-API misuse guard fired before any subagent process is spawned.
Source
Thrown at ts/packages/cli/src/services/run-helpers-runtime.ts:360
const normalizeInvokeAgentOptions = (
options: Record<string, unknown> = {}
): InvokeAgentNormalizedOptions => {
if (options == null || typeof options !== 'object' || Array.isArray(options)) {
throw new Error('experimental_subAgent() options must be an object when provided.');
}
if (options.schema !== undefined && options.jsonSchema !== undefined) {
throw new Error(
'experimental_subAgent() accepts either options.schema or options.jsonSchema, not both.'
);
}
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;View on GitHub (pinned to 64b1b85502)
Solutions
- Set target to exactly "claude", "codex", or "user" (lowercase)
- Omit the target property entirely to auto-detect the current master agent
- If target comes from user input, validate/narrow it before calling experimental_subAgent
Example fix
// before
await experimental_subAgent('summarize', { target: 'Claude' });
// after
await experimental_subAgent('summarize', { target: 'claude' }); Defensive patterns
Strategy: type-guard
Validate before calling
const TARGETS = ['claude','codex','user'] as const;
if (opts.target !== undefined && !TARGETS.includes(opts.target)) throw new RangeError(`bad target: ${opts.target}`); Type guard
const isSubagentTarget = (v: unknown): v is 'claude'|'codex'|'user' => v === 'claude' || v === 'codex' || v === 'user';
Try / catch
catch (e) { if (e instanceof Error && e.message.includes('target must be')) { /* fix the literal and retry */ } throw e; } Prevention
- Type the options object as { target?: 'claude'|'codex'|'user' }
- Keep target as a compile-time literal, not runtime-computed strings
When it happens
Trigger: Calling experimental_subAgent(prompt, { target: ... }) with a typo'd or unsupported value, e.g. { target: 'Claude' } (capitalized), { target: 'openai' }, or a variable typed as string that isn't narrowed to the union.
Common situations: Passing a dynamically-built target string, copying a target name from docs of a different tool, or case mismatches. Also passing target: undefined explicitly is fine, but null or empty string throws.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- experimental_subAgent() schema must be a Zod schema or JSON
- experimental_subAgent() requires a non-empty prompt string.
- Invalid Composio CLI release tag: ${releaseTag}
- Invalid
- Invalid
AI-assisted analysis of ComposioHQ/composio@64b1b85502 (2026-08-28).
Data as JSON: /api/errors/1d32b9dd6f58d07c.
Report an issue: GitHub.