n8n-io/n8n · error · Error

${toolName} policy.maxChildren must be a finite positive int

Error message

${toolName} policy.maxChildren must be a finite positive integer

What it means

`resolveDelegateSubAgentPolicy` validates the `maxChildren` field of the delegation policy, which controls how many child sub-agent runs may execute in parallel. It defaults to `DEFAULT_SUB_AGENT_MAX_CHILDREN` (10) when undefined, then checks `Number.isFinite` and `Number.isInteger`. This throws when `maxChildren` is `NaN`, `Infinity`, `-Infinity`, a float (e.g. 2.5), or a non-number type that survived into the resolved policy.

Source

Thrown at packages/@n8n/agents/src/runtime/tools/delegate-sub-agent-tool.ts:375

	toModelOutput?: (output: z.infer<typeof delegateSubAgentOutputSchema>) => unknown;
}

export type DelegateSubAgentToolMetadata = CreateDelegateSubAgentToolOptions;

function resolveDelegateSubAgentPolicy(
	policy: DelegateSubAgentPolicy | undefined,
	toolName: string,
): DelegateSubAgentPolicy {
	const resolvedPolicy = {
		...policy,
		maxChildren: policy?.maxChildren ?? DEFAULT_SUB_AGENT_MAX_CHILDREN,
	};

	if (
		!Number.isFinite(resolvedPolicy.maxChildren) ||
		!Number.isInteger(resolvedPolicy.maxChildren)
	) {
		throw new Error(`${toolName} policy.maxChildren must be a finite positive integer`);
	}

	if (resolvedPolicy.maxChildren < 1) {
		throw new Error(`${toolName} policy.maxChildren must be at least 1`);
	}

	return resolvedPolicy;
}

const DELEGATE_SUB_AGENT_TOOL_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]{0,63}$/;

function resolveDelegateSubAgentToolName(name: string | undefined): string {
	if (name === undefined) return DELEGATE_SUB_AGENT_TOOL_NAME;
	if (!DELEGATE_SUB_AGENT_TOOL_NAME_PATTERN.test(name)) {
		throw new Error(
			`Invalid delegate sub-agent tool name "${name}": must start with a letter and contain only letters, digits, underscores, and hyphens (max 64 characters)`,
		);
	}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Check the `maxChildren` value being passed — log it before the factory call.
  2. Coerce to an integer before passing: `Math.floor(Number(value))`, and validate it is finite.
  3. If the value comes from user input, validate with a Zod schema (`z.number().int().finite().positive()`) before it reaches the policy resolver.
  4. Omit `maxChildren` entirely if you want the default of 10.

Example fix

// before:
createDelegateSubAgentTool({ policy: { maxChildren: 2.5 } });

// after:
createDelegateSubAgentTool({ policy: { maxChildren: Math.floor(userInput) } });
// or omit for default:
createDelegateSubAgentTool({ }); // maxChildren defaults to 10
Defensive patterns

Strategy: validation

Validate before calling

function validateMaxChildren(value: unknown): number {
  const n = Number(value);
  if (!Number.isFinite(n) || !Number.isInteger(n)) {
    throw new Error('maxChildren must be a finite integer');
  }
  return n;
}

const maxChildren = validateMaxChildren(policy?.maxChildren);
createDelegateSubAgentTool({ policy: { maxChildren } });

Type guard

function isFinitePositiveInteger(value: unknown): value is number {
  return typeof value === 'number' && Number.isFinite(value) && Number.isInteger(value);
}

Prevention

When it happens

Trigger: Calling the delegate sub-agent tool factory with `policy: { maxChildren: NaN }`, `policy: { maxChildren: Infinity }`, `policy: { maxChildren: 2.5 }`, or `policy: { maxChildren: '5' as any }`. Also when `maxChildren` is parsed from user/config input that was not coerced to an integer.

Common situations: A UI or config field for 'max parallel sub-agents' accepted free-text and passed an uncoerced value. A JSON config had `maxChildren: 2.5` or `maxChildren: null` that was not caught upstream. Floating-point division produced a non-integer (e.g. `totalTasks / workers`).

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/5492b41019040446. Report an issue: GitHub.