n8n-io/n8n · error · Error
Webhook request failed: ${response.status} ${response.status
Error message
Webhook request failed: ${response.status} ${response.statusText} What it means
The webhook delivery `fetch` returned a non-2xx HTTP status; the thrown message includes `response.status` and `response.statusText`. Anything from the receiver — auth failure, payload rejected, server error — surfaces here. The URL is masked in logs just above the throw, so secrets in the path/query are not leaked. Note this only covers HTTP-level failures; network errors throw from `fetch` itself and are not wrapped.
Source
Thrown at packages/@n8n/ai-workflow-builder.ee/evaluations/cli/webhook.ts:259
logger.info('Webhook request will be signed with HMAC-SHA256');
} else {
logger.warn(
'No webhook secret provided - request will not be signed. ' +
'Consider using --webhook-secret for production use.',
);
}
// Log masked URL to avoid exposing potential tokens in path/query
logger.info(`Sending results to webhook: ${maskWebhookUrl(webhookUrl)}`);
const response = await fetch(webhookUrl, {
method: 'POST',
headers,
body,
});
if (!response.ok) {
throw new Error(`Webhook request failed: ${response.status} ${response.statusText}`);
}
logger.info(`Webhook notification sent successfully (status: ${response.status})`);
}
View on GitHub (pinned to 5ac6606e81)
Solutions
- Match the status code to the cause: 401/403 → fix `--webhook-secret` / signature header name; 404 → fix URL; 4xx body → align payload schema; 5xx → check receiver health.
- Verify the receiver logs the incoming `X-Webhook-Signature` / `X-Webhook-Timestamp` headers and validates them with the same HMAC scheme `generateWebhookSignature` uses (`sha256=<hex>`).
- Reproduce with curl using the masked URL plus the same body to confirm whether the issue is the client or the receiver.
Example fix
// before
// receiver returns 401 because it expects a different header
const response = await fetch(webhookUrl, {method:'POST', headers, body});
if (!response.ok) throw new Error(`Webhook request failed: ${response.status} ${response.statusText}`);
// after
// align header names with receiver (e.g. X-Hub-Signature-256) and re-sign
headers['X-Hub-Signature-256'] = generateWebhookSignature(signaturePayload, webhookSecret); Defensive patterns
Strategy: try-catch
Try / catch
try {
await sendWebhookResult({ webhookUrl, webhookSecret, payload, logger });
} catch (e) {
if (e instanceof Error && e.message.startsWith('Webhook request failed:')) {
const status = e.message;
if (status.includes('401') || status.includes('403')) {
logger.error('webhook auth failed — check --webhook-secret and receiver signature scheme');
} else if (status.startsWith('Webhook request failed: 5')) {
// receiver-side fault: bounded retry with backoff is reasonable
await backoffRetry(() => sendWebhookResult({ webhookUrl, webhookSecret, payload, logger }), { tries: 3 });
} else {
logger.error(`webhook delivery failed: ${status}`);
}
return; // do not fail the whole eval run on a webhook error
}
throw e;
} Prevention
- Decouple webhook delivery from eval success: catch delivery errors so a flaky receiver does not waste a completed run.
- Document the exact signature scheme (header names, `sha256=<hex>` format) the receiver expects so secrets align.
- Add an integration test that posts to a throwaway receiver to catch schema/signature drift early.
When it happens
Trigger: `fetch(webhookUrl, {method:'POST',...})` resolves but `response.ok` is false. Concretely: 401/403 (missing or wrong `--webhook-secret`), 404 (wrong URL/path), 422 (schema mismatch in `WebhookPayload`), 5xx (receiver down).
Common situations: Receiver expects a different signature scheme than HMAC-SHA256; the receiver's payload schema changed but the eval still sends the old shape; the URL drifted; rate limiting returns 429; an authenticated proxy in front of the receiver rejects the unsigned request.
Related errors
- Failed to list ${provider} models (status ${response.status}
- LangSmith sessions lookup failed: ${res.status} ${await res.
- LangSmith runs/query failed: ${res.status} ${await res.text(
- n8n API ${method} ${path} failed (${res.status}): ${text}
- Authorization is required!
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/604f305a537a8e29.
Report an issue: GitHub.