n8n-io/n8n · error · AxiosError

Request failed with status code

Error message

Request failed with status code ${response.status}

What it means

AxiosError thrown by throwIfStatusRejected inside followSsrfRedirects when the terminal (non-redirect) response fails the caller's validateStatus policy. It mirrors the code axios itself derives in settle: 4xx -> ERR_BAD_REQUEST, 5xx -> ERR_BAD_RESPONSE. Because the redirect layer runs with maxRedirects:0 and a permissive validateStatus for 3xx, only the final response reaches this check.

Solutions

  1. Read the embedded response.status to classify (4xx client error vs 5xx server error).
  2. If the caller supplied a custom validateStatus, relax it to accept the returned status.
  3. For 5xx, retry with backoff; for 4xx, fix the request URL/params/auth.
  4. Inspect error.response.body for the upstream service's own error detail.

Example fix

// before - default validateStatus rejects 3xx-not-redirect and anything >= 300
const res = await followSsrfRedirects(config, policy);

// after - accept 4xx as a normal response and handle inline
const res = await followSsrfRedirects(
  { ...config, validateStatus: (s) => s >= 200 && s < 500 },
  policy,
);
if (res.status >= 400) { /* handle upstream error body */ }
Defensive patterns

Strategy: try-catch

Validate before calling

// Decide which statuses your caller can handle, then pass validateStatus
const validateStatus = (s: number) => s >= 200 && s < 400; // accept redirects handled by the layer

Type guard

import { isAxiosError } from 'axios';
const isStatusRejected = (e: unknown): boolean =>
  isAxiosError(e) && (e.code === 'ERR_BAD_REQUEST' || e.code === 'ERR_BAD_RESPONSE');

Try / catch

try {
  const res = await followSsrfRedirects(config, policy);
} catch (e) {
  if (isAxiosError(e) && typeof e.response?.status === 'number') {
    if (e.response.status >= 500) { /* retry with backoff */ }
    else if (e.response.status >= 400) { /* client error — fix request */ }
  }
  throw e;
}

Prevention

When it happens

Trigger: followSsrfRedirects receives a non-3xx response (or a 3xx without a Location) whose status is rejected by baseValidateStatus (default: status < 200 or >= 300). throwIfStatusRejected then re-throws an AxiosError carrying config, request, and response for compatibility with axios consumers.

Common situations: A node HTTP request returns 404/500 on the final hop; the caller passed a custom validateStatus that rejects the returned status; the target endpoint is down; SSRF-validated target returns an error after redirects.

Related errors


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

Appendix: source

Thrown at packages/@n8n/backend-network/src/http/axios/redirect.ts:192

			headerPatternsToDropOnRedirect(downgradeToGet, stripCredentials),
		),
		...buildRedirectHopAgents(nextUrl, policy),
	};
}

/**
 * Enforces the caller's status policy on a terminal response, throwing the same `AxiosError` axios would.
 */
function throwIfStatusRejected(
	response: AxiosResponse,
	validateStatus: (status: number) => boolean,
): void {
	if (!validateStatus(response.status)) {
		// Same code axios derives in `settle`: 4xx -> ERR_BAD_REQUEST, 5xx -> ERR_BAD_RESPONSE, else undefined.
		const code = [AxiosError.ERR_BAD_REQUEST, AxiosError.ERR_BAD_RESPONSE][
			Math.floor(response.status / 100) - 4
		];
		throw new AxiosError(
			`Request failed with status code ${response.status}`,
			code,
			response.config,
			response.request,
			response,
		);
	}
}

/**
 * Resolves a redirect `Location` against the current URL.
 * @throws OperationalError when the server returns a malformed Location that cannot be resolved.
 */
function resolveRedirectUrl(location: string, currentUrl: string): string {
	try {
		return new URL(location, currentUrl).href;
	} catch {
		throw new OperationalError(`Invalid redirect location received from server: ${location}`);

View on GitHub (pinned to 5ac6606e81)