Skip to content

Conversation

@Avaq
Copy link

@Avaq Avaq commented Oct 23, 2025

Type

  • Refactor
  • Feature
  • Bug Fix
  • Optimization
  • Documentation Update

Description

This allows users to customise how HttpApiDecodeErrors are represented on the wire, enabling the use of HttpApi for wire protocols that don't follow the Tagged Error approach to encoding errors.

Related

@Avaq Avaq requested a review from tim-smart as a code owner October 23, 2025 15:33
@github-project-automation github-project-automation bot moved this to Discussion Ongoing in PR Backlog Oct 23, 2025
@changeset-bot
Copy link

changeset-bot bot commented Oct 23, 2025

⚠️ No Changeset found

Latest commit: fc20fc2

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@gcanti
Copy link
Contributor

gcanti commented Nov 3, 2025

I'm not sure this will work as intended, in handlerToRoute within HttpApiBuilder.ts there's a HttpApiDecodeError.refailParseError, which makes me think the error logic is fixed

@Avaq
Copy link
Author

Avaq commented Nov 3, 2025

@gcanti yes -- it converts ParseError to HttpApiDecodeError. But at a later point, the (now overridable) schema is used to encode that HttpApiDecodeError for the wire. So no matter the origin of the HttpApiDecodeError, the schema is still going to work, as far as I understand it.

At least in my limited testing, it worked for both the OpenApi output and the encoding of Decode Errors.

@gcanti
Copy link
Contributor

gcanti commented Nov 5, 2025

I see, I'll let @tim-smart chime in then

@Avaq
Copy link
Author

Avaq commented Nov 5, 2025

I initially worked on a version where the user-supplied type of the encoded HttpApiDecodeError was threaded as a generic type through the HttpApp, but eventually found that this wasn't necessary.

And the user supplied schema doesn't even need to be lossless, it's fine to "encode" every HttpApiDecodeError to the value 42 and just have a decoder that always fails, as long as there's a schema for 42 added with addError. That way the HttpClient will be able to decode the weirdly encoded errors by just failing over to the schema that's able to decode it, as with any Union decoder.

@Avaq
Copy link
Author

Avaq commented Nov 30, 2025

@tim-smart could you please take a look at this? Most of the diff is just a new test fixture, the actual change is just ~10 loc.

export const make = <const Id extends string>(identifier: Id): HttpApi<Id, never, HttpApiDecodeError> =>
export const make = <const Id extends string>(
identifier: Id,
errorSchema: Schema.Schema<HttpApiDecodeError, any> = HttpApiDecodeError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally this would be an options object, and instead of any you can use an discarded generic.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I try to use a discarded generic, TypeScript issues:

Type 'typeof HttpApiDecodeError' is not assignable to type 'Schema<HttpApiDecodeError, E, never>'. Types of property 'Encoded' are incompatible. Type '{ readonly _tag: "HttpApiDecodeError"; readonly message: string; readonly issues: readonly { readonly _tag: "Type" | "Pointer" | "Unexpected" | "Missing" | "Composite" | "Refinement" | "Transformation" | "Forbidden"; readonly message: string; readonly path: readonly (string | ... 1 more ... | { ...; })[]; }[]; }' is not assignable to type 'E'. 'E' could be instantiated with an arbitrary type which could be unrelated to '{ readonly _tag: "HttpApiDecodeError"; readonly message: string; readonly issues: readonly { readonly _tag: "Type" | "Pointer" | "Unexpected" | "Missing" | "Composite" | "Refinement" | "Transformation" | "Forbidden"; readonly message: string; readonly path: readonly (string | ... 1 more ... | { ...; })[]; }[]; }'. (ts 2322) 

It's because allowing a user to supply E may invalidate the default parameter we're trying to assign.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated to use an options object

@Avaq Avaq force-pushed the avaq/override-decode-error-schema branch from 1dda206 to 69e0599 Compare December 1, 2025 10:39
This allows users to customize how HttpApiDecodeErrors are represented on the wire, enabling the use of HttpApi for wire protocols that don't follow the Tagged Error approach to encoding errors.
@Avaq Avaq force-pushed the avaq/override-decode-error-schema branch from 69e0599 to fc20fc2 Compare December 1, 2025 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

3 participants