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
- Read the embedded status and body text to classify the failure (auth vs not-found vs server).
- For 401/403, re-login and retry; for 404, verify the resource id; for 5xx, retry with backoff or surface the incident.
- 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
- Branch on N8nApiError.status, not on message substrings.
- Re-login on 401/403 before retrying.
- Wrap fetch in a retry-with-backoff for 5xx.
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
- Failed to list ${provider} models (status ${response.status}
- Webhook request failed: ${response.status} ${response.status
- LangSmith sessions lookup failed: ${res.status} ${await res.
- LangSmith runs/query failed: ${res.status} ${await res.text(
- Failed to authenticate with n8n — no session cookie received
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/d88e497109b96e8c.
Report an issue: GitHub.