Errors

HTTP/API failures recognized by a preset, invalid JSON, and schema failures use ServiceError. Network failures, cancellation, and your own thrown Error objects are not converted to ServiceError.

import { Http, ServiceError } from "fetchzen";
const api = Http.create("https://api.example.com", { cache: false });
try {
await api.get("/users/1");
} catch (error) {
if (error instanceof ServiceError) {
console.error(error.status, error.message, error.errors);
} else if (error instanceof Error && error.name === "AbortError") {
console.info("Request canceled");
} else {
throw error;
}
}
PropertyMeaning
message, status, statusTextDetails selected by the preset; API fields may override the HTTP status.
errorsA string, string array, or object with an optional description.
codeOptional machine-readable code; not every API code is copied here.
responseOptional Fetch Response; use response.status for the actual HTTP status.
requestOptional { url, method }; included for normal preset and schema failures.
causeOriginal error when available, such as a JSON parsing SyntaxError.

toJSON() includes diagnostic fields and request metadata, but omits response and cause. Review messages, URLs, and error details before sending them to a log service.

Existing Error instances keep their identity, stack, and cause unless an error hook replaces them. Non-Error thrown values are wrapped in an Error with the original value as cause.

Each HTTP method has a safe counterpart with the same arguments and preset shape:

import { Http, ServiceError } from "fetchzen";
const api = Http.create("https://api.example.com", { cache: false });
const { data: result, error } = await api.safe.get<{ id: number }>("/me");
if (error) {
console.error(error.message);
if (error instanceof ServiceError) {
console.error(error.status, error.errors);
}
} else {
console.log(result.payload.id);
}

The result is { data: result, error: undefined } on success or { data: undefined, error: Error } on failure. Both properties are readonly in the TypeScript union. data keeps the preset-shaped result unchanged; it does not unwrap payload. Narrow the error before accessing ServiceError-specific properties. safe does not disable validation or error hooks. It wraps the HTTP helpers and the low-level request() primitive through the same pipeline.

const { data, error: requestError } = await api.safe.request({
method: "GET",
endpoint: "/health",
responseType: "text",
});
if (!requestError) {
console.log(data.payload); // string
}

safe.request() supports the same configuration, preset shapes, schema-output inference, and response-mode inference as request(). The ordinary request() still rejects on failure. Configure defaults and register hooks on api, not its safe facade.

CodeMeaning
ERR_INVALID_JSONJSON parsing failed. errors.description holds up to the first 200 characters; cause is the parsing error.
ERR_SCHEMASchema validation returned issues, flattened into errors.

Body parsing runs before preset validation. Invalid JSON on a 500 response can therefore produce ERR_INVALID_JSON instead of the preset’s HTTP error. A timeout normally has the name TimeoutError; default caller cancellation uses AbortError. A caller may supply another abort reason. See resilience.

A synchronous thrower replaces preset validation, including its HTTP status check. Delegate to presetThrower to keep the usual checks:

import { envelope, Http, presetThrower, ServiceError } from "fetchzen";
const api = Http.create("https://api.example.com", { cache: false });
const fallback = presetThrower(envelope);
await api.get("/legacy", {
thrower: ({ json, response }) => {
if (json.legacyError) {
throw new ServiceError({
message: "Legacy API failure",
status: response.status,
statusText: response.statusText,
errors: [],
response,
});
}
fallback({ json, response });
},
});

Use the client’s actual preset when constructing the fallback. For non-JSON responses, json is an empty object. Schema validation still follows the thrower. For reusable API rules, prefer preset conventions. Try safe errors or the low-level safe request.