Effect-TS / effect

An ecosystem of tools to build robust applications in TypeScript
https://effect.website
MIT License
7.71k stars 245 forks source link

HttpApiClient Allow for non-tagged errors / don't die on parse errors. #3987

Closed florianbepunkt closed 3 days ago

florianbepunkt commented 3 days ago

What is the problem this feature would solve?

We incrementally introduce effect in an existing codebase. We have an HttpApi where authorization is done on the application level (using effect code), but authentication is done by AWS by an API gateway authorizer. This authorizer can throw an 401 error that is not tagged.

Currently, when the API returns a non-tagged error (as in our case with the AWS api authorizer), there is no way to handle that error. The program dies with:

ParseError: (Unauthorized (Encoded side) <-> Unauthorized)
└─ Encoded side transformation failure
   └─ Unauthorized (Encoded side)
      └─ ["_tag"]
         └─ is missing

https://github.com/Effect-TS/effect/blob/3686ff644f57a7d8ed3a498f521cadfa70267c1e/packages/platform/src/HttpApiClient.ts#L187

Discord thread: https://discord.com/channels/795981131316985866/1098177242598756412/threads/1309819953482039356

What is the feature you are proposing to solve the problem?

Either a request should not die on a ParseError or that should be made configurable. Personally I would prefer if the client never dies on its own without the possibility to change that behavior and catch/handle errors.

What alternatives have you considered?

In this case I think there are none, since a third-party system is involved where we have no control over the returned error.

florianbepunkt commented 3 days ago

Thanks to the really great community on discord, this has been resolved.

Problem origin: We had an Unauthorized error defined in our codebase as a tagged error like this

export class Unauthenticated extends S.TaggedError<Unauthenticated>()(
  "Unauthenticated",
  {},
  HttpApiSchema.annotations({ status: 401 }),
) {}

Since the error from AWS was not tagged, this led to the parse error.

Defining the error like this works:

class UnauthFromAWS extends S.Class<UnauthFromAWS>("Schema")({
  message: S.String,
}) {
  readonly _tag = "UnauthFromAWS"; // being added only on decoded side
}
api.addError(UnauthFromAWS, { status: 401 })