Skip to content

HttpApiClient reports undeclared HTTP statuses as DecodeError even when the response body is valid #9866

Closed

Description

@nick-inkeep

What version of Effect is running?

effect@4.0.6, @effect/platform-node@4.0.6, and @effect/platform-node-shared@4.0.6 from npm; Node v24.22.1 on macOS 27.0 arm64. main at b72f6a28 has the same behavior.

What steps can reproduce the bug?

Define an endpoint with a declared 409 error, then return an undeclared 418 with a valid JSON body:

import { Cause, Effect, Schema } from "effect"
import { HttpClient, HttpClientResponse } from "effect/http"
import { HttpApi, HttpApiClient, HttpApiEndpoint, HttpApiGroup, HttpApiSchema } from "effect/http-api"

const ErrorBody = Schema.Struct({ message: Schema.String }).pipe(HttpApiSchema.status(409))
const Api = HttpApi.make("Api").add(
  HttpApiGroup.make("test").add(
    HttpApiEndpoint.get("get", "/test", { error: ErrorBody })
  )
)

const httpClient = HttpClient.make((request) =>
  Effect.succeed(HttpClientResponse.fromWeb(request, new Response(
    JSON.stringify({ message: "valid but undeclared" }),
    { status: 418, headers: { "content-type": "application/json" } }
  )))
)

const client = await Effect.runPromise(HttpApiClient.makeWith(Api, {
  baseUrl: "http://test",
  httpClient
}))
const exit = await Effect.runPromiseExit(client.test.get({}))
if (exit._tag === "Failure") {
  const error = Cause.squash(exit.cause)
  console.log(error.reason._tag, error.response?.status)
}

Expected output for the undeclared status is a status-code classification such as StatusCodeError; a distinct unexpected-status reason would also avoid labeling it as a decode failure. Actual output is DecodeError 418.

The same client returns DecodeError for an undeclared 418 with not-json, so the fallback does not depend on a body-decoding failure.

What is the expected behavior?

DecodeError is documented as a response error “for failures while decoding an HTTP response body.” An undeclared status should be classified as a status-code rejection such as StatusCodeError, or use a separate reason for an unexpected status.

What do you see instead?

HttpApiClient dispatches an undeclared status to statusOrElse, which immediately constructs HttpClientError with DecodeError without reading the body. The implementation is at HttpApiClient.ts#L393-L393 and HttpApiClient.ts#L573-L573, with the fallback at HttpApiClient.ts#L1239-L1247. The reason documentation defines StatusCodeError as status-code rejection and DecodeError as body-decoding failure at HttpClientError.ts#L194-L236.

Additional information

A declared 409 whose body fails to decode returns StatusCodeError together with the schema cause, which is consistent with the status-code reason. The mismatch is specific to the undeclared-status fallback.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions