Fault variants and factory helpers.
Errors
Execute returns a discriminated Fault in the tuple's first slot — not a thrown exception for
declared failures.
FaultCode
type FaultCode =
| "CAP_BUFFER_EXCEEDED"
| "CAP_QUEUE_OVERFLOW"
| "ENV_UNSUPPORTED"
| "EXT_HOOK_FAILED"
| "EXT_INTERCEPTOR_FAILED"
| "EXT_OBSERVER_FAILED"
| "HTTP_STATUS"
| "NET_ABORTED"
| "NET_BODY_INCOMPLETE"
| "NET_TIMEOUT"
| "NET_UNREACHABLE"
| "REQ_BUILD_FAILED"
| "REQ_INPUT_INVALID"
| "REQ_OPTIONS_INVALID"
| "RES_DECODE_FAILED"
| "RES_MEDIA_TYPE_INVALID"
| "RES_STRUCT_MISMATCH";One closed set. The segment before the first _ is the class; there is no separate kind field.
The set stays closed so that switch (fault.code) is exhaustive — extensions report their own
detail through cause, not through a new code.
FaultClass
type FaultClass<C extends string> = C extends `${infer TClass}_${string}`
? TClass
: never;FaultClass<FaultCode> is 'CAP' | 'ENV' | 'EXT' | 'HTTP' | 'NET' | 'REQ' | 'RES'.
Fault
type Fault<TErr extends AnyStruct | undefined = undefined> = FaultOf<
ErrorBody<TErr>
>;Switch on fault.code.
FaultOf
type FaultOf<TData = undefined> =
DecodeFault | HttpStatusFaultOf<TData> | PreflightFault;
type Fault<TErr extends AnyStruct | undefined = undefined> = FaultOf<
ErrorBody<TErr>
>;FaultOf is Fault expressed by the decoded error-body type instead of the struct that produced it. HttpAwaitResult and the fault factories work in decoded values, so they use this form.
Every variant is a native Error named DefjsFault, so String(fault) produces
DefjsFault: <message>. code and the variant metadata — status, response, data —
are enumerable own properties. The native cause chain is non-enumerable.
Default Struct messages say received <kind>, never the received value, for example Expected number at email, received string. A JSON SyntaxError behind RES_DECODE_FAILED gives the message Response body could not be decoded. Issue paths are structural and retain record keys: struct.record(...) can produce Expected number at alice@example.com, received string, so keys may still be sensitive in default messages, prettify(), and String(fault). Raw StructIssue.received and parser causes remain sensitive; custom errorMap and extension messages are used verbatim and need review before logging.
import { StructError, type Fault } from "@defjs/core";
function logFault(fault: Fault): void {
console.error(String(fault), { code: fault.code });
if (fault.cause instanceof StructError) {
console.error(fault.cause.prettify());
}
}Call format(), flatten(), or prettify() only after narrowing fault.cause to StructError;
those helpers are not copied onto the fault.
HttpStatusFault
type HttpStatusFaultOf<TData> = Error & {
code: "HTTP_STATUS";
data: TData;
response: [TData] extends [undefined] ? HttpMeta : DecodedResponse<TData>;
status: number;
};
type HttpStatusFault<TErr extends AnyStruct | undefined = undefined> =
HttpStatusFaultOf<ErrorBody<TErr>>;Any non-2xx status. When the endpoint declared error and the body decoded, data is that body
and response carries it. When error was omitted the body is never read, which leaves data as
undefined and response as metadata alone.
DecodeFault
type DecodeFaultCode =
"RES_DECODE_FAILED" | "RES_MEDIA_TYPE_INVALID" | "RES_STRUCT_MISMATCH";
type DecodeFault = Error & {
cause: unknown;
code: DecodeFaultCode;
response: HttpMeta;
status: number;
};A response arrived but could not be read as declared. response is metadata only: a body is a
decoded value, and decoding is what failed, so there is no body field to reach for. The offending
detail lives on cause — a StructError for a struct mismatch, the parser failure for an
unreadable representation.
PreflightFault
type PreflightFaultCode = Exclude<
FaultCode,
DecodeFaultCode | "HTTP_STATUS"
>;
type PreflightFault = Error & {
cause?: unknown;
code: PreflightFaultCode;
response?: HttpMeta;
};Everything that may have failed before a response existed: REQ_*, NET_*, EXT_*, CAP_*,
ENV_UNSUPPORTED. response is present only where the transport already had metadata to report,
such as a body that truncated mid-download.
AnyFault
type AnyFault =
| DecodeFault
| HttpStatusFaultOf<undefined>
| HttpStatusFaultOf<unknown>
| PreflightFault;Any fault regardless of its decoded error-body type. Use it for handlers that classify faults
without caring which endpoint produced them; prefer Fault<typeof yourErrorStruct> where the body
type matters.
Factories
createHttpStatusFault()
createUndecodedHttpStatusFault()
createDecodeFault()
createNetworkFault()
createPreflightFault()
function createHttpStatusFault<TData>(
response: DecodedResponse<TData>,
): HttpStatusFaultOf<TData>;
function createUndecodedHttpStatusFault(
response: HttpMeta,
): HttpStatusFaultOf<undefined>;
function createDecodeFault(
code: DecodeFaultCode,
cause: unknown,
response: HttpMeta,
): DecodeFault;
function createNetworkFault(
cause: unknown,
response?: HttpMeta,
): PreflightFault;
function createPreflightFault(
code: PreflightFaultCode,
cause?: unknown,
response?: HttpMeta,
): PreflightFault;createHttpStatusFault takes a response that already carries the decoded body;
createUndecodedHttpStatusFault takes metadata alone, for an endpoint that declared no error.
createNetworkFault uses isAbortCause for NET_ABORTED and isTimeoutCause for NET_TIMEOUT: these recognize branded sentinels from any Core copy, a DOMException named AbortError, and a DOMException or Error named TimeoutError, respectively. Everything else maps to NET_UNREACHABLE. createPreflightFault takes the code directly; with no
cause, the code becomes the message.
All factories return native Error instances with the structured fields above; they do not create
plain object errors and need no adapter for String(fault).
Sentinels
ERR_ABORTED
ERR_TIMEOUT
const ERR_ABORTED: Error;
const ERR_TIMEOUT: Error;Shared Error sentinel objects used as cause for abort and timeout. Throwing one from an HTTP interceptor
resolves the call with NET_ABORTED or NET_TIMEOUT. SSE and WebSocket interceptors report any
throw, these included, as EXT_INTERCEPTOR_FAILED.
The sentinels carry non-enumerable Symbol.for('defjs.cause.aborted') and Symbol.for('defjs.cause.timeout') brands. Classification does not depend on the sentinel's object identity or unrelated earlier executions.
Cause checks
function isAbortCause(cause: unknown): boolean;
function isTimeoutCause(cause: unknown): boolean;isAbortCause recognizes an abort-branded Error or a DOMException named AbortError. isTimeoutCause recognizes a timeout-branded Error or a DOMException / Error named TimeoutError. Pass fault.cause to classify the underlying value; these functions do not unwrap a fault. They work with sentinels from another Core copy.
See Errors guide.