n8n-io/n8n · error · Error

toolCallConcurrency must be a positive integer or Infinity

Error message

toolCallConcurrency must be a positive integer or Infinity

What it means

Thrown by Agent.toolCallConcurrency(n) when n is not a positive integer and not Infinity. The check rejects zero, negative numbers, fractional numbers, NaN, and non-integer values. This controls how many tool calls within a single LLM turn execute in parallel, so it must be a valid positive count.

Source

Thrown at packages/@n8n/agents/src/sdk/agent.ts:548

		}
		return this;
	}

	/** @internal Read the declared telemetry builder (used by the execution engine to resolve credentials). */
	protected get declaredTelemetry(): Telemetry | undefined {
		return this.telemetryBuilder;
	}

	/**
	 * Set the number of tool calls to execute concurrently within a single LLM turn.
	 *
	 * - `1` (default) — sequential execution, fully backward-compatible.
	 * - `Infinity` — unlimited parallelism (all tool calls start at once).
	 * - Any number in between — bounded concurrency (e.g. `5` = at most 5 tools run simultaneously).
	 */
	toolCallConcurrency(n: number): this {
		if ((n !== Infinity && !Number.isInteger(n)) || n < 1) {
			throw new Error('toolCallConcurrency must be a positive integer or Infinity');
		}
		this.concurrencyValue = n;
		return this;
	}

	/**
	 * Attach a workspace to this agent. Workspace tools and instructions
	 * are injected at build time.
	 */
	workspace(ws: Workspace): this {
		this.workspaceInstance = ws;
		return this;
	}

	/**
	 * Add an MCP client as a tool source for this agent.
	 * Tools from all servers in the client become available to the agent.
	 * Multiple clients can be added; tools are merged across all of them.

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pass 1 for sequential execution (the default), a positive integer for bounded concurrency, or Infinity for unlimited.
  2. If reading from config, validate and coerce: const n = parseInt(raw, 10); if (Number.isInteger(n) && n >= 1) agent.toolCallConcurrency(n).
  3. Use a fallback default of 1 when the config value is missing or invalid.

Example fix

// before
const raw = process.env.TOOL_CONCURRENCY; // undefined or '0'
agent.toolCallConcurrency(parseInt(raw)); // NaN or 0 -> throws
// after
const n = Number.parseInt(process.env.TOOL_CONCURRENCY ?? '1', 10);
agent.toolCallConcurrency(Number.isFinite(n) && n >= 1 ? n : 1);
Defensive patterns

Strategy: validation

Validate before calling

function isValidConcurrency(n: unknown): boolean {
  return n === Infinity || (typeof n === 'number' && Number.isInteger(n) && n >= 1);
}
const concurrency = isValidConcurrency(rawValue) ? (rawValue as number) : 1;
agent.toolCallConcurrency(concurrency);

Type guard

function isPositiveIntegerOrInfinity(n: unknown): n is number {
  return n === Infinity || (typeof n === 'number' && Number.isInteger(n) && n >= 1);
}

Prevention

When it happens

Trigger: Calling agent.toolCallConcurrency(0), toolCallConcurrency(-1), toolCallConcurrency(2.5), toolCallConcurrency(NaN), or passing a value from user input that was not validated as a positive integer.

Common situations: Reading concurrency from an environment variable or config file without parsing or validating (e.g. parseInt returning NaN for a missing value). Defaulting to 0 meaning 'disabled'. Passing a float from a slider or ratio calculation. Distinguishing 'sequential' (1) from 'off' (0) incorrectly.

Related errors


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