[GH-ISSUE #5110] Typesafe error handling with the server-side auth.api #18795

Closed
opened 2026-04-15 17:27:29 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @nathan-hello on GitHub (Oct 6, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/5110

For the client api, there is authClient.$ERROR_CODES that I can check against and get all of the copy for, as shown in this bit of docs https://www.better-auth.com/docs/concepts/client#error-codes

Is there something similar to the server api? When I call auth.api.<method>, instead of returning an error it throws an APIError. I don't really have a problem with this by itself, but is there any way to know what can throw what? Even one large array of codes like in the authClient would be good.

I'm migrating away from authClient to have everything on the server, and now I don't know what to check for.

The below bit of code is my server-side handler for /auth/logout. You can find the rest of the project here. For context, in react-router-v7 framework mode, you can throw a Response object and the server will just send that response instead of going forward with the normal route handler, and redirect is just a function that returns a 302. This is my server logic for signing someone out.

I need to check if someone is not signed in, because I assume that giving signOut headers without an auth token would make it throw, but I also need to wrap getSession in a try/catch because in the case that I'm checking against, it would also throw.

export async function loader({ request }: Route.LoaderArgs): Promise<AuthError[] | undefined> {
  try {
    const session = await auth.api.getSession({ headers: request.headers });
    if (session === null) {
      throw redirect("/");
    }
  } catch (error) {
    if (error instanceof Response) {
      throw error;
    }
    // ignore APIErrors, because we in fact want there to not be a session
  }
  try {
    const { headers, response } = await auth.api.signOut({ headers: request.headers, returnHeaders: true });
    if (response.success === false) {
      throw Error("generic_error");
    }
    throw redirect("/", { headers });
  } catch (error) {
    if (error instanceof Response) {
      throw error;
    }

    const errors = getAuthError(error);
    return errors;
  }
}

In here, I don't want to pass the headers without any session into signOut because I assume it will throw, but I can't call getSession without it maybe throwing if there isn't a session!

I'm not really upset by this block of code, I'm not looking for advice on making it "cleaner", I really just want there to be some typescript-level way to know what errors I might get from an auth.api call, thrown or returned, more than just "it's an APIError". There are tons of error codes that I have gotten that aren't in the authClient.$ERROR_CODES string union, that I need to make copy for, that I don't really see any way to know about beforehand in the docs.

This is something I could potentially participate in, if it's just a matter of tracking which methods throw what APIError objects and building some typesafety around that, I could find that nice to do. Really just wondering how everyone else handles the many errors that pop up on the server.

Thank you for reading.

Originally created by @nathan-hello on GitHub (Oct 6, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/5110 For the client api, there is `authClient.$ERROR_CODES` that I can check against and get all of the copy for, as shown in this bit of docs https://www.better-auth.com/docs/concepts/client#error-codes Is there something similar to the server api? When I call `auth.api.<method>`, instead of returning an error it throws an `APIError`. I don't really have a problem with this by itself, but is there any way to know what can throw what? Even one large array of codes like in the authClient would be good. I'm migrating away from `authClient` to have everything on the server, and now I don't know what to check for. The below bit of code is my server-side handler for `/auth/logout`. You can find the rest of the project [here](https://github.com/nathan-hello/bauth-server). For context, in react-router-v7 framework mode, you can `throw` a `Response` object and the server will just send that response instead of going forward with the normal route handler, and `redirect` is just a function that returns a 302. This is my server logic for signing someone out. I need to check if someone is not signed in, because I assume that giving `signOut` headers without an auth token would make it throw, but I also need to wrap `getSession` in a try/catch because in the case that I'm checking against, it would also throw. ```ts export async function loader({ request }: Route.LoaderArgs): Promise<AuthError[] | undefined> { try { const session = await auth.api.getSession({ headers: request.headers }); if (session === null) { throw redirect("/"); } } catch (error) { if (error instanceof Response) { throw error; } // ignore APIErrors, because we in fact want there to not be a session } try { const { headers, response } = await auth.api.signOut({ headers: request.headers, returnHeaders: true }); if (response.success === false) { throw Error("generic_error"); } throw redirect("/", { headers }); } catch (error) { if (error instanceof Response) { throw error; } const errors = getAuthError(error); return errors; } } ``` In here, I don't want to pass the headers without any session into `signOut` because I assume it will throw, but I can't call `getSession` without it maybe throwing if there isn't a session! I'm not really upset by this block of code, I'm not looking for advice on making it "cleaner", I really just want there to be some typescript-level way to know what errors I might get from an `auth.api` call, thrown or returned, more than just "it's an APIError". There are tons of error codes that I have gotten that aren't in the `authClient.$ERROR_CODES` string union, that I need to make copy for, that I don't really see any way to know about beforehand in the docs. This is something I could potentially participate in, if it's just a matter of tracking which methods throw what `APIError` objects and building some typesafety around that, I could find that nice to do. Really just wondering how everyone else handles the many errors that pop up on the server. Thank you for reading.
GiteaMirror added the lockedenhancement labels 2026-04-15 17:27:29 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Oct 6, 2025):

Hi @nathan-hello! I'm Dosu and I’m helping the better-auth team.

There isn't a centralized, typesafe, programmatically-exported list of all possible server-side error codes like authClient.$ERROR_CODES. Instead, server-side error codes are defined in per-feature constants (for example, BASE_ERROR_CODES, ORGANIZATION_ERROR_CODES, ADMIN_ERROR_CODES, and similar objects for plugins), each using a utility function for structure enforcement. These are scattered across the codebase and not aggregated into a single union or array for global introspection—so you have to manually import and merge them if you want a complete set for typesafe handling or copy mapping [source].

When you call auth.api.<method>, errors are thrown as APIError instances, often with codes like 'UNAUTHORIZED', 'BAD_REQUEST', or plugin-specific codes, and the message usually comes from one of these error code objects [source]. There is no built-in way to know at compile time exactly which error codes a given method might throw.

If you want to build typesafety or copy mapping for server-side error codes, you can aggregate the relevant error code objects manually. For example:

import { BASE_ERROR_CODES } from "better-auth/src/error/codes";
import { ORGANIZATION_ERROR_CODES } from "better-auth/src/plugins/organization/error-codes";
import { ADMIN_ERROR_CODES } from "better-auth/src/plugins/admin/error-codes";
// ...import other plugin error codes as needed

const ALL_ERROR_CODES = {
  ...BASE_ERROR_CODES,
  ...ORGANIZATION_ERROR_CODES,
  ...ADMIN_ERROR_CODES,
  // ...spread others here
} as const;

type ServerAuthErrorCode = keyof typeof ALL_ERROR_CODES;

This gives you a union type of all known error codes for use in your own error handling and copy logic. If you want to contribute a utility to aggregate these automatically, it would be a helpful addition for the community.

For practical error handling, most users catch APIError in their route handlers or middleware, inspect the code and message properties, and shape responses accordingly. There are examples of this pattern in community threads, but everyone currently relies on manual aggregation and inspection for server-side error codes [source].

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

<!-- gh-comment-id:3369608388 --> @dosubot[bot] commented on GitHub (Oct 6, 2025): <!-- Greeting --> Hi @nathan-hello! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> There isn't a centralized, typesafe, programmatically-exported list of all possible server-side error codes like `authClient.$ERROR_CODES`. Instead, server-side error codes are defined in per-feature constants (for example, `BASE_ERROR_CODES`, `ORGANIZATION_ERROR_CODES`, `ADMIN_ERROR_CODES`, and similar objects for plugins), each using a utility function for structure enforcement. These are scattered across the codebase and not aggregated into a single union or array for global introspection—so you have to manually import and merge them if you want a complete set for typesafe handling or copy mapping [[source](https://github.com/better-auth/better-auth/blob/ad59590e2491359a2787d75b3209b02351ff99de/packages/better-auth/src/error/codes.ts)]. When you call `auth.api.<method>`, errors are thrown as `APIError` instances, often with codes like `'UNAUTHORIZED'`, `'BAD_REQUEST'`, or plugin-specific codes, and the message usually comes from one of these error code objects [[source](https://github.com/better-auth/better-auth/blob/ad59590e2491359a2787d75b3209b02351ff99de/packages/better-auth/src/api/routes/sign-in.ts)]. There is no built-in way to know at compile time exactly which error codes a given method might throw. If you want to build typesafety or copy mapping for server-side error codes, you can aggregate the relevant error code objects manually. For example: ```ts import { BASE_ERROR_CODES } from "better-auth/src/error/codes"; import { ORGANIZATION_ERROR_CODES } from "better-auth/src/plugins/organization/error-codes"; import { ADMIN_ERROR_CODES } from "better-auth/src/plugins/admin/error-codes"; // ...import other plugin error codes as needed const ALL_ERROR_CODES = { ...BASE_ERROR_CODES, ...ORGANIZATION_ERROR_CODES, ...ADMIN_ERROR_CODES, // ...spread others here } as const; type ServerAuthErrorCode = keyof typeof ALL_ERROR_CODES; ``` This gives you a union type of all known error codes for use in your own error handling and copy logic. If you want to contribute a utility to aggregate these automatically, it would be a helpful addition for the community. For practical error handling, most users catch `APIError` in their route handlers or middleware, inspect the `code` and `message` properties, and shape responses accordingly. There are examples of this pattern in community threads, but everyone currently relies on manual aggregation and inspection for server-side error codes [[source](https://github.com/better-auth/better-auth/issues/3722)]. <!-- Dosu Comment Footer --> *To reply, just mention [@dosu](https://go.dosu.dev/dosubot).* --- <sup>How did I do? [Good](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=other)</sup>&nbsp;&nbsp;[![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/5110)
Author
Owner

@nathan-hello commented on GitHub (Oct 6, 2025):

Hi @nathan-hello! I'm Dosu and I’m helping the better-auth team.

There isn't a centralized, typesafe, programmatically-exported list of all possible server-side error codes like authClient.$ERROR_CODES. Instead, server-side error codes are defined in per-feature constants (for example, BASE_ERROR_CODES, ORGANIZATION_ERROR_CODES, ADMIN_ERROR_CODES, and similar objects for plugins), each using a utility function for structure enforcement. These are scattered across the codebase and not aggregated into a single union or array for global introspection—so you have to manually import and merge them if you want a complete set for typesafe handling or copy mapping [source].

When you call auth.api.<method>, errors are thrown as APIError instances, often with codes like 'UNAUTHORIZED', 'BAD_REQUEST', or plugin-specific codes, and the message usually comes from one of these error code objects [source]. There is no built-in way to know at compile time exactly which error codes a given method might throw.

If you want to build typesafety or copy mapping for server-side error codes, you can aggregate the relevant error code objects manually. For example:

import { BASE_ERROR_CODES } from "better-auth/src/error/codes";
import { ORGANIZATION_ERROR_CODES } from "better-auth/src/plugins/organization/error-codes";
import { ADMIN_ERROR_CODES } from "better-auth/src/plugins/admin/error-codes";
// ...import other plugin error codes as needed

const ALL_ERROR_CODES = {
...BASE_ERROR_CODES,
...ORGANIZATION_ERROR_CODES,
...ADMIN_ERROR_CODES,
// ...spread others here
} as const;

type ServerAuthErrorCode = keyof typeof ALL_ERROR_CODES;
This gives you a union type of all known error codes for use in your own error handling and copy logic. If you want to contribute a utility to aggregate these automatically, it would be a helpful addition for the community.

For practical error handling, most users catch APIError in their route handlers or middleware, inspect the code and message properties, and shape responses accordingly. There are examples of this pattern in community threads, but everyone currently relies on manual aggregation and inspection for server-side error codes [source].

To reply, just mention @dosu.

How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Chat with Dosu Join Discord Share on X

BASE_ERROR_CODES isn't exported from what I can tell, but it's okay because what I was looking for was type AuthApiErrors = keyof typeof auth["$ERROR_CODES"]. This has all of the error codes I want, with the plugins, in one string union.

This library is crazy good. Thanks!

<!-- gh-comment-id:3369656673 --> @nathan-hello commented on GitHub (Oct 6, 2025): > Hi [@nathan-hello](https://github.com/nathan-hello)! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. > > There isn't a centralized, typesafe, programmatically-exported list of all possible server-side error codes like `authClient.$ERROR_CODES`. Instead, server-side error codes are defined in per-feature constants (for example, `BASE_ERROR_CODES`, `ORGANIZATION_ERROR_CODES`, `ADMIN_ERROR_CODES`, and similar objects for plugins), each using a utility function for structure enforcement. These are scattered across the codebase and not aggregated into a single union or array for global introspection—so you have to manually import and merge them if you want a complete set for typesafe handling or copy mapping [[source](https://github.com/better-auth/better-auth/blob/ad59590e2491359a2787d75b3209b02351ff99de/packages/better-auth/src/error/codes.ts)]. > > When you call `auth.api.<method>`, errors are thrown as `APIError` instances, often with codes like `'UNAUTHORIZED'`, `'BAD_REQUEST'`, or plugin-specific codes, and the message usually comes from one of these error code objects [[source](https://github.com/better-auth/better-auth/blob/ad59590e2491359a2787d75b3209b02351ff99de/packages/better-auth/src/api/routes/sign-in.ts)]. There is no built-in way to know at compile time exactly which error codes a given method might throw. > > If you want to build typesafety or copy mapping for server-side error codes, you can aggregate the relevant error code objects manually. For example: > > import { BASE_ERROR_CODES } from "better-auth/src/error/codes"; > import { ORGANIZATION_ERROR_CODES } from "better-auth/src/plugins/organization/error-codes"; > import { ADMIN_ERROR_CODES } from "better-auth/src/plugins/admin/error-codes"; > // ...import other plugin error codes as needed > > const ALL_ERROR_CODES = { > ...BASE_ERROR_CODES, > ...ORGANIZATION_ERROR_CODES, > ...ADMIN_ERROR_CODES, > // ...spread others here > } as const; > > type ServerAuthErrorCode = keyof typeof ALL_ERROR_CODES; > This gives you a union type of all known error codes for use in your own error handling and copy logic. If you want to contribute a utility to aggregate these automatically, it would be a helpful addition for the community. > > For practical error handling, most users catch `APIError` in their route handlers or middleware, inspect the `code` and `message` properties, and shape responses accordingly. There are examples of this pattern in community threads, but everyone currently relies on manual aggregation and inspection for server-side error codes [[source](https://github.com/better-auth/better-auth/issues/3722)]. > > _To reply, just mention [@dosu](https://go.dosu.dev/dosubot)._ > > How did I do? [Good](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/88da880f-97dd-4a50-8c77-10d6a0690436?feedback_type=other)  [![Chat with Dosu](https://camo.githubusercontent.com/d9ffebb979729c9ef0fc4e3090dbae5b17cabbfec8afe378ada6b81913d6ad8a/68747470733a2f2f646f73752e6465762f646f73752d636861742d62616467652e737667)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github) [![Join Discord](https://camo.githubusercontent.com/d562c2df5e4567ea41d37f1d91a4da0839d8341037045b103b2da268998de17f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6a6f696e2d3538363546323f6c6f676f3d646973636f7264266c6f676f436f6c6f723d7768697465266c6162656c3d)](https://go.dosu.dev/discord-bot) [![Share on X](https://camo.githubusercontent.com/4649f705e2396d6f328dbc8e800a0509c9c17b3b20253b36706c393df124b93d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f582d73686172652d626c61636b)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/better-auth/better-auth/issues/5110) BASE_ERROR_CODES isn't exported from what I can tell, but it's okay because what I was looking for was `type AuthApiErrors = keyof typeof auth["$ERROR_CODES"]`. This has all of the error codes I want, with the plugins, in one string union. This library is crazy good. Thanks!
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#18795