One function. Every error.
normalizeError turns Axios failures, failed fetches, GraphQL errors, Node transport errors, and thrown strings into a single predictable object — so your UI, retry logic, and logging code never need client-specific handling again.
Any error shape
Normalize Axios, Fetch, ky, Apollo, Node.js transport errors, thrown strings, and raw backend payloads.
Consistent output
Always returns { code, message, status, retryable, details } — the same shape regardless of what threw.
Zero config
Drop in normalizeError and handle every edge case without wrappers, adapters, or configuration.
Retry intelligence
The retryable flag lets you build retry logic in one place instead of inferring it from status codes per-client.
TypeScript-first
Fully typed output so your error handlers stay compile-safe across every client and scenario.
Preserve originals
The details field keeps the raw error accessible for Sentry, Datadog, or any other observability pipeline.
Every HTTP client throws differently
Axios wraps errors in AxiosError with a response object. Fetch never throws on HTTP errors — you check response.ok yourself. ky throws HTTPError. GraphQL returns an errors array, not an exception. Node transport errors carry a code field like ECONNRESET instead of a status. Without a normalizer, every catch block needs client-specific logic that breaks when you switch libraries.
// Brittle: every client needs its own logic
try {
await axios.get("/api/data");
} catch (err) {
if (err.response?.status === 503) {
setMsg(err.response.data.message ?? "Down");
scheduleRetry();
} else if (err.code === "ECONNRESET") {
setMsg("Connection lost");
} else if (typeof err === "string") {
setMsg(err);
} else {
setMsg("Something went wrong");
}
}// Clean: one function, every client
import { normalizeError } from "@npmforge/statusforge";
try {
await axios.get("/api/data");
} catch (err) {
const { message, retryable } = normalizeError(err);
setMsg(message);
if (retryable) scheduleRetry();
}Consistent output, every time
No matter what threw — or whether anything threw at all — you always get the same five fields back.
codestring"SERVICE_UNAVAILABLE"Machine-readable token for switch/match logic and error tracking.messagestring"Service temporarily unavailable"Human-readable explanation, safe to render directly in UI.statusnumber | null503HTTP status code when available; null for transport or non-HTTP errors.retryablebooleantrueWhether retrying the request is likely to succeed.detailsunknown{ ... }Original error value preserved for logging and debugging.Handles all of these out of the box
Live Playground
HTTP clientAxios HTTP 503 error
Run a scenario to inspect its raw error value.
Run a scenario to inspect normalized output.
Get started in seconds
Install StatusForge and normalize every failure path the same way.
npm i @npmforge/statusforge