When working with HttpClient, you’ll encounter a small set of well-defined error types. Understanding them is essential for writing robust error handling code.
HttpClientError
HttpClientError is the single error class for all HTTP client errors. Every error from the client is an HttpClientError with _tag: "HttpClientError". What varies is the reason, which is a union of six tagged types, split into two categories: request errors (failures before the server responds) and response errors (failures after the server responds).
You can check whether an unknown value is an HttpClientError using the isHttpClientError guard:
import { HttpClientError } from "effect/unstable/http";
HttpClientError.isHttpClientError(someValue); // true or falseRequest Errors
Request errors occur before the server sends a response. The request either couldn’t be built or couldn’t be sent.
TransportError: A network-level failure. The connection timed out, DNS resolution failed, the server refused the connection, or the network is unreachable.EncodeError: The request body couldn’t be serialized. This typically happens ifbodyJsonencounters data thatJSON.stringifycan’t handle.InvalidUrlError: The URL couldn’t be parsed into a valid URL object.
Every request error includes the original request object, so you always know which request failed.
import { FetchHttpClient, HttpClient, HttpClientRequest,} from "effect/unstable/http";import { Effect } from "effect";
const program = Effect.gen(function* () { const client = yield* HttpClient.HttpClient;
// This will fail with a TransportError (invalid host) const request = HttpClientRequest.get("https://this-does-not-exist.invalid"); const response = yield* client.execute(request);
return yield* response.json;}).pipe( Effect.catch((error) => { console.log("Tag:", error._tag); console.log("Reason:", error.reason._tag); console.log("Request:", error.reason.methodAndUrl); console.log("Message:", error.reason.message); return Effect.void; }), Effect.provide(FetchHttpClient.layer),);
// Test itEffect.runPromise(program);Output:
Tag: HttpClientErrorReason: TransportErrorRequest: GET https://this-does-not-exist.invalidMessage: Transport error (GET https://this-does-not-exist.invalid)Notice the structure. The outer error._tag is always "HttpClientError". The error.reason._tag tells you the specific failure type. Properties like methodAndUrl and message live on the reason object.
Response Errors
Response errors occur after the server sends a response but something went wrong when processing it.
It has a reason field with these possible values:
StatusCodeError: The response came back with a status code your code considers unacceptable. For example, a404or500. This error is produced when you explicitly opt in to status code checking usingfilterStatusOk,filterStatus, or similar filters, which are covered in a later chapter.DecodeError: The response body couldn’t be parsed. For example, callingresponse.jsonon a response that isn’t valid JSON.EmptyBodyError: You tried to read a body that doesn’t exist (e.g., a 204 No Content response).
Every response error includes both the original request and the response, giving you full context.
import { FetchHttpClient, HttpClient, HttpClientRequest,} from "effect/unstable/http";import { Effect } from "effect";
const program = Effect.gen(function* () { const client = (yield* HttpClient.HttpClient).pipe(HttpClient.filterStatusOk);
// This returns a 404, which filterStatusOk turns into a StatusCodeError const request = HttpClientRequest.get("https://dummyjson.com/users/9999"); const response = yield* client.execute(request);
return yield* response.json;}).pipe( Effect.catch((error) => { console.log("Tag:", error._tag); console.log("Reason:", error.reason._tag); if (error.response) { console.log("Status:", error.response.status); } console.log("Request:", error.reason.methodAndUrl); console.log("Message:", error.reason.message); return Effect.void; }), Effect.provide(FetchHttpClient.layer),);
// Test itEffect.runPromise(program);Output:
Tag: HttpClientErrorReason: StatusCodeErrorStatus: 404Request: GET https://dummyjson.com/users/9999Message: StatusCode: non 2xx status code (404 GET https://dummyjson.com/users/9999)SchemaError
SchemaError comes from Effect’s Schema module, not from HttpClient itself. You’ll encounter it when using schema-based validation like HttpClientResponse.schemaBodyJson() or HttpClientResponse.schemaHeaders().
When the response body or headers don’t match your schema, the validation step fails with a SchemaError. This error is separate from HttpClientError. It’s a schema validation failure, not an HTTP failure.
import { FetchHttpClient, HttpClient, HttpClientRequest, HttpClientResponse,} from "effect/unstable/http";import { Effect, Schema } from "effect";
// A schema that expects fields the API doesn't returnconst WrongSchema = Schema.Struct({ nonExistentField: Schema.String,});
const program = Effect.gen(function* () { const client = yield* HttpClient.HttpClient;
const request = HttpClientRequest.get("https://dummyjson.com/users/1");
const response = yield* client.execute(request);
// This will fail because the response doesn't match our schema const data = yield* HttpClientResponse.schemaBodyJson(WrongSchema)(response);
return data;}).pipe( Effect.catch((error) => { console.log("Tag:", error._tag); console.log("Message:", error.message); return Effect.void; }), Effect.provide(FetchHttpClient.layer),);
// Test itEffect.runPromise(program);Output:
Tag: SchemaErrorMessage: Missing key at ["nonExistentField"]