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

  1. Set target to exactly "claude", "codex", or "user" (lowercase)
  2. Omit the target property entirely to auto-detect the current master agent
  3. 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

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


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