Skip to content

Commit 5dd8c1c

Browse files
fix: do not invent Unknown error on successful executions
1 parent c4a2f69 commit 5dd8c1c

4 files changed

Lines changed: 99 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Fixed
11+
12+
- **`n8n_executions` `mode=error` no longer invents "Unknown error" on a successful run** ([#1065](https://github.com/czlonkowski/n8n-mcp/issues/1065)). When the execution has no `resultData.error` and no node-level error, error mode now reports `success: true` with a "nothing to diagnose" note instead of blaming the last node.
13+
1014
## [2.82.1] - 2026-09-03
1115

1216
### Fixed

src/services/error-execution-processor.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,31 @@ export function processErrorExecution(
9191
const runData = resultData?.runData as Record<string, any> || {};
9292
const lastNode = resultData?.lastNodeExecuted;
9393

94+
if (!hasDiagnosableError(error, runData)) {
95+
const executionPath = includeExecutionPath
96+
? buildObservedExecutionPath(runData)
97+
: undefined;
98+
return {
99+
success: true,
100+
primaryError: {
101+
message: 'No error found in this execution',
102+
errorType: 'None',
103+
nodeName: lastNode || '',
104+
nodeType: '',
105+
},
106+
executionPath,
107+
suggestions: [
108+
{
109+
type: 'investigate',
110+
title: 'Nothing to diagnose',
111+
description:
112+
'This execution has no error payload and no node-level error. Use summary or filtered mode to inspect outputs, or pick an execution whose status is error.',
113+
confidence: 'high',
114+
},
115+
],
116+
};
117+
}
118+
94119
// 1. Extract primary error info
95120
const primaryError = extractPrimaryError(error, lastNode, runData, includeStackTrace);
96121

@@ -125,6 +150,48 @@ export function processErrorExecution(
125150
};
126151
}
127152

153+
/**
154+
* True when there is a real error payload or a node-level error.
155+
* A successful execution with only lastNodeExecuted must not invent one.
156+
*/
157+
function hasDiagnosableError(
158+
error: Record<string, unknown> | undefined,
159+
runData: Record<string, any>
160+
): boolean {
161+
if (error && (error.message || error.node || error.stack || error.name)) {
162+
return true;
163+
}
164+
for (const data of Object.values(runData)) {
165+
if (getRunError(data)) {
166+
return true;
167+
}
168+
}
169+
return false;
170+
}
171+
172+
/**
173+
* Path of nodes that actually ran, with their real status.
174+
* Does not mark the last node as failed.
175+
*/
176+
function buildObservedExecutionPath(
177+
runData: Record<string, any>
178+
): ErrorAnalysis['executionPath'] {
179+
const nodesByTime = Object.entries(runData)
180+
.map(([name, data]) => ({
181+
name,
182+
data: data as any[],
183+
startTime: latestStartTime(data),
184+
}))
185+
.sort((a, b) => a.startTime - b.startTime);
186+
187+
return nodesByTime.map(({ name, data }) => ({
188+
nodeName: name,
189+
status: getRunError(data) ? 'error' : 'success',
190+
itemCount: countRunItems(data),
191+
executionTime: totalExecutionTime(data),
192+
}));
193+
}
194+
128195
/**
129196
* Extract primary error information
130197
*/

src/types/n8n-api.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -619,6 +619,12 @@ export interface FilteredNodeData {
619619

620620
// Error Mode Types
621621
export interface ErrorAnalysis {
622+
/**
623+
* True when error mode was requested but the execution has no error to
624+
* diagnose (status success, no resultData.error, no node-level error).
625+
*/
626+
success?: boolean;
627+
622628
// Primary error information
623629
primaryError: {
624630
message: string;

tests/unit/services/error-execution-processor.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,28 @@ function createMockWorkflow(options?: {
176176
* Core Functionality Tests
177177
*/
178178
describe('ErrorExecutionProcessor - Core Functionality', () => {
179+
it('should not invent Unknown error on a successful execution (#1065)', () => {
180+
const execution = createMockExecution({
181+
errorNode: 'Last Node',
182+
hasExecutionError: false,
183+
runData: {
184+
Trigger: createSuccessfulNodeData(1),
185+
'Process Data': createSuccessfulNodeData(5),
186+
'Last Node': createSuccessfulNodeData(2),
187+
},
188+
});
189+
execution.status = ExecutionStatus.SUCCESS;
190+
191+
const result = processErrorExecution(execution, { includeExecutionPath: true });
192+
193+
expect(result.success).toBe(true);
194+
expect(result.primaryError.message).toBe('No error found in this execution');
195+
expect(result.primaryError.errorType).toBe('None');
196+
expect(result.primaryError.nodeName).toBe('Last Node');
197+
expect(result.executionPath?.every(step => step.status !== 'error')).toBe(true);
198+
expect(result.suggestions?.[0].title).toBe('Nothing to diagnose');
199+
});
200+
179201
it('should extract primary error information', () => {
180202
const execution = createMockExecution({
181203
errorNode: 'HTTP Request',

0 commit comments

Comments
 (0)