n8n-io/n8n · error · TypeError

workflow() third argument is settings, not workflow structur

Error message

workflow() third argument is settings, not workflow structure. Do not pass nodes or connections here — use .add() and .to() to build the workflow. Example: workflow('id', 'Name').add(trigger({...})).to(node({...}))

What it means

The third argument to workflow() is for WorkflowSettings (timezone, executionOrder, etc.) or a WorkflowBuilderOptions object (settings + registry). If the builder detects 'nodes' or 'connections' keys on that argument, it assumes the caller tried to pass a full workflow structure (the shape consumed by fromJSON) and throws, because workflow() builds incrementally via .add()/.to(). The guard prevents a silent no-op where settings would be ignored.

Source

Thrown at packages/@n8n/workflow-sdk/src/workflow-builder.ts:1331

		);
	}
	if (typeof name !== 'string') {
		const receivedName = Array.isArray(name) ? 'an array' : typeof name;
		throw new TypeError(
			// eslint-disable-next-line n8n-local-rules/no-interpolation-in-regular-string
			'workflow() requires (id: string, name: string). ' +
				`workflow() requires a string name as second argument, but received ${receivedName}. ` +
				"Example: workflow('my-workflow-id', 'My Workflow Name')",
		);
	}
	if (
		options !== undefined &&
		(Array.isArray(options) ||
			(typeof options === 'object' &&
				options !== null &&
				('nodes' in options || 'connections' in options)))
	) {
		throw new TypeError(
			'workflow() third argument is settings, not workflow structure. ' +
				'Do not pass nodes or connections here — use .add() and .to() to build the workflow. ' +
				"Example: workflow('id', 'Name').add(trigger({...})).to(node({...}))",
		);
	}
	if (isWorkflowBuilderOptions(options)) {
		return new WorkflowBuilderImpl(
			id,
			name,
			options.settings,
			undefined,
			undefined,
			undefined,
			undefined,
			options.registry,
		);
	}
	return new WorkflowBuilderImpl(id, name, options);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. To import an existing workflow structure, use workflow.fromJSON({ nodes, connections, ... }) instead of workflow().
  2. If you intended to pass settings, pass only setting keys (timezone, executionOrder, saveExecutionProgress, etc.) or a { settings, registry } options object.
  3. Build the workflow incrementally: workflow('id', 'name').add(trigger({})).to(node({})).

Example fix

// before
workflow('id', 'name', { nodes: [...], connections: {...} }); // throws

// after (import)
workflow.fromJSON({ id: 'id', name: 'name', nodes: [...], connections: {...} });

// after (settings)
workflow('id', 'name', { timezone: 'UTC' });
Defensive patterns

Strategy: validation

Validate before calling

function isWorkflowStructure(v: unknown): boolean {
  return typeof v === 'object' && v !== null && ('nodes' in v || 'connections' in v);
}
if (isWorkflowStructure(thirdArg)) {
  // use fromJSON instead of workflow()
}

Type guard

function isWorkflowStructure(v: unknown): v is { nodes: unknown[]; connections: unknown } {
  return typeof v === 'object' && v !== null && ('nodes' in v || 'connections' in v);
}

Prevention

When it happens

Trigger: Passing { nodes: [...], connections: {...} } as the third argument to workflow(), typically because the caller confused workflow() with fromJSON() or pasted a full workflow definition into the factory.

Common situations: Migrating from a JSON-import flow to the builder API and forgetting to switch from workflow(jsonStructure) to fromJSON(jsonStructure); copy-pasting a workflow body into the factory call.

Related errors


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