Skip to content

Failure model ​

Handle failures where the operation completes. Receiving an HTTP response, decoding JSON, and consuming an event stream are separate operations; a successful earlier stage does not guarantee the next one.

FailureWhere it appearsApplication decision
Network rejectionRequest interceptor pipeline, normally wrapped in ExchangeErrorInspect the underlying cause and decide whether replay is safe
Non-2xx HTTP statusDefault status validation rejects with HttpStatusValidationError itself, an ExchangeError carrying the exchangeHandle the service's error response and status; HTTP 2xx still needs business-result checks
JSON or custom extractionAfter the exchange pipeline; rejects directly to request() callerCatch around result consumption and validate the fields you use
Error interceptor itself throwsDirectly out of error handlingDo not assume every rejection is ExchangeError
SSE read/transform failureDuring asynchronous stream iterationHandle partial output and dispose the reader

Default status validation: packages/fetcher/src/validateStatusInterceptor.ts:169. Wrapping, rethrow and recovery: packages/fetcher/src/interceptorManager.ts:204. Extraction: packages/fetcher/src/fetcher.ts:246 and packages/fetcher/src/resultExtractor.ts:69. A status failure matches instanceof HttpStatusValidationError directly. A timeout or network failure is wrapped, so an outer instanceof FetchTimeoutError check misses it; inspect the ExchangeError cause as shown in the failure guide and error reference.

Timeout and cancellation are different controls ​

Request optionsTransport behaviorDeadline owner
signal or abortController supplied, timeout enabledLibrary combines them with its timer; whichever fires first aborts the FetchLibrary clears its timer after Fetch settles; the timer never aborts the caller's controller
Neither supplied, timeout enabledLibrary aborts the Fetch with its own timerLibrary clears its timer; nothing is written to the request
Timeout disabledNative Fetch, aborted by the supplied signal or abortController if presentCaller

The combination is implemented in packages/fetcher/src/timeout.ts:184. The timer ends when the Fetch race settles, not when subsequent response.json() or the whole SSE stream finishes. A timeout rejects with FetchTimeoutError; a caller abort rejects with the caller's abort reason. For a whole-operation deadline, own the signal and the full consumption lifecycle. Follow cancellation guidance.

Cancellation does not roll back a write already received by the server. Before retrying after a timeout or lost response, use the endpoint's idempotency and result-reconciliation contract.

Hooks change how errors reach components ​

useExecutePromise stores errors in state, and execute() never rejects: it resolves to the state the execution ended in (success, error, or idle when cancelled), so a caller branches on status instead of catching. An error named AbortError, such as one from a signal of your own, returns to idle. onSuccess/onError run only for the current execution; a throwing callback is reported with console.error and does not change state. A new execution, abort(), reset() and unmount abort the execution's controller, but a generic Promise must cooperate with that controller. useFetcher additionally exposes the failed request's exchange from an ExchangeError, for example to read a 404 status. See packages/react/src/core/useExecutePromise.ts:107 and React request guide.

Streams and authentication have specific recovery contracts ​

SSE JSON transformation calls JSON.parse; unhandled transformer errors become stream errors. Read failures surface during iteration. Early return or break cancels the reader and releases its lock; normal completion also releases the lock. The initial exchange succeeding says nothing about the remaining stream. There is no automatic reconnect, deduplication, or exactly-once delivery guarantee. See packages/eventstream/src/jsonServerSentEventTransformStream.ts:80, packages/eventstream/src/safeTransformer.ts:58, packages/eventstream/src/readableStreamAsyncIterable.ts:102, and SSE consumption reference.

The core default pipeline does not provide a general retry policy. CoSec's bounded 401 refresh path checks its own authorization header and token session, refreshes, then replays the original exchange. That is an authentication protocol, not arbitrary-error retry. Request-body replayability, write idempotency, and server outcomes remain application concerns. A failed refresh ends the session only when the refresh endpoint rejects the refresh token (a 4xx, or a response that is not a composite token): the token is removed and RefreshTokenError reaches onUnauthorized. A network error, timeout, abort or 5xx keeps the session and rejects with RefreshUnavailableError, which signs no one out; a later request refreshes again (packages/cosec/src/jwtTokenManager.ts:54). See packages/cosec/src/authorizationResponseInterceptor.ts:80, CoSec guide, and token refresh reference.

Released under the Apache License 2.0.