n8n-io/n8n · error · N8nApiError

n8n API ${method} ${path} failed (${res.status}): ${text}

Error message

n8n API ${method} ${path} failed (${res.status}): ${text}

What it means

The internal fetch wrapper throws N8nApiError whenever res.ok is false, embedding method, path, status, and response body. This is the generic surface for any non-2xx from the n8n REST API and the parent class for distinguishable server failures.

Source

Thrown at packages/@n8n/instance-ai/evaluations/clients/n8n-client.ts:1035

		// A bare `?? DEFAULT` would turn `timeoutMs: 0` into `AbortSignal.timeout(0)` —
		// an instant abort, where the old truthiness check meant "unbounded". No caller
		// passes one, and unbounded is what this path exists to remove, so a
		// non-positive value falls back to the default: bounded either way.
		const timeoutMs =
			options.timeoutMs !== undefined && options.timeoutMs > 0
				? options.timeoutMs
				: DEFAULT_REQUEST_TIMEOUT_MS;

		const res = await fetch(`${this.baseUrl}${path}`, {
			method,
			headers,
			body: options.body ? JSON.stringify(options.body) : undefined,
			signal: AbortSignal.timeout(timeoutMs),
		});

		if (!res.ok) {
			const text = await res.text();
			throw new N8nApiError(
				`n8n API ${method} ${path} failed (${res.status}): ${text}`,
				res.status,
			);
		}

		// Capture auth cookie from login response
		const setCookie = res.headers.get('set-cookie');
		if (setCookie) {
			const match = setCookie.match(/n8n-auth=[^;]+/);
			if (match) {
				this.sessionCookie = match[0];
			}
		}

		return await res.json();
	}
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Read the embedded status and body text to classify the failure (auth vs not-found vs server).
  2. For 401/403, re-login and retry; for 404, verify the resource id; for 5xx, retry with backoff or surface the incident.
  3. Catch N8nApiError specifically and branch on its `.status` field rather than parsing the message.

Example fix

try {
  await client.fetch('/rest/foo', { method: 'GET' });
} catch (e) {
  if (e instanceof N8nApiError && e.status === 401) { await client.login(); /* retry */ }
  else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight reachability:
const healthz = await fetch(`${base}/healthz`).catch(() => null);
if (!healthz || !healthz.ok) throw new Error('n8n unreachable before call');

Type guard

const isN8nApiError = (e: unknown): e is { status: number } =>
  e instanceof Error && typeof (e as any).status === 'number';

Try / catch

try { return await client.fetch(path, opts); }
catch (e) {
  if (e instanceof N8nApiError && (e.status === 401 || e.status === 403)) { await client.login(); /* retry */ }
  else if (e instanceof N8nApiError && e.status >= 500) { /* backoff retry */ }
  else throw e;
}

Prevention

When it happens

Trigger: Any n8n REST call returning 4xx/5xx: missing/invalid credentials (401/403), not-found (404), validation (400), rate limit (429), or server errors (5xx).

Common situations: Expired session causing 401s; referencing a deleted resource (404); sending malformed bodies (400); n8n under load returning 5xx.

Related errors


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