From 6cf3b0b67900dc1c8cd46d2bf2c223ea938651fc Mon Sep 17 00:00:00 2001 From: Jaakko Husso Date: Mon, 1 Jun 2026 00:35:08 +0300 Subject: [PATCH] fix(core): Coerce non-string node names in buildNodeIndex (#31411) --- .../__tests__/submit-workflow.tool.test.ts | 17 +++++++++++++++++ .../src/tools/workflows/submit-workflow.tool.ts | 7 ++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/submit-workflow.tool.test.ts b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/submit-workflow.tool.test.ts index 5f1a29a7196..6520652c6c1 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/__tests__/submit-workflow.tool.test.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/__tests__/submit-workflow.tool.test.ts @@ -438,6 +438,23 @@ describe('buildNodeIndex', () => { it('returns an empty array when nodes is missing', () => { expect(buildNodeIndex({ connections: {} } as unknown as WorkflowJSON)).toEqual([]); }); + + it('coerces non-string node names to an empty string', () => { + const json = { + nodes: [ + { name: 123, type: 'x', position: [0, 0], parameters: {} }, + { type: 'y', position: [0, 0], parameters: {} }, + { name: null, type: 'z', position: [0, 0], parameters: {} }, + ], + connections: {}, + } as unknown as WorkflowJSON; + + expect(buildNodeIndex(json)).toEqual([ + { index: 0, name: '' }, + { index: 1, name: '' }, + { index: 2, name: '' }, + ]); + }); }); describe('buildErrorDetails', () => { diff --git a/packages/@n8n/instance-ai/src/tools/workflows/submit-workflow.tool.ts b/packages/@n8n/instance-ai/src/tools/workflows/submit-workflow.tool.ts index f5df1af4cdc..2e7a88b22cd 100644 --- a/packages/@n8n/instance-ai/src/tools/workflows/submit-workflow.tool.ts +++ b/packages/@n8n/instance-ai/src/tools/workflows/submit-workflow.tool.ts @@ -116,7 +116,12 @@ export function extractStructureIssues(error: unknown): WorkflowStructureIssue[] * counting entries in its own SDK-builder code. */ export function buildNodeIndex(json: WorkflowJSON): Array<{ index: number; name: string }> { - return (json.nodes ?? []).map((node, index) => ({ index, name: node.name ?? '' })); + // Defensively coerce: a malformed workflow may carry a non-string node name, + // which would otherwise violate the `z.string()` output schema downstream. + return (json.nodes ?? []).map((node, index) => ({ + index, + name: typeof node.name === 'string' ? node.name : '', + })); } /**