Include error code in error types for passkey result in client auth #1665

Closed
opened 2026-03-13 08:54:37 -05:00 by GiteaMirror · 5 comments
Owner

Originally created by @discoverlance-com on GitHub (Aug 8, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Create a backend
  2. Use Passkey plugin
export const auth = betterAuth({
 ...,
  plugins: [
    passkey({}),
  ],
  1. Add Passkey plugin to client auth
export const authClient = createAuthClient({
 ...,
  plugins: [
    passkeyClient(),
  ]
})
  1. Call the addPasskey function on the auth client
const result = await authClient.passkey.addPasskey({
      name: values.passkey_name,
      authenticatorAttachment: "cross-platform",
    });
  1. You will need to trigger some kind of failure, in my case, the session was not fresh. Log the result.error
// the type of result.error
error: {
    message?: string | undefined;
    status: number;
    statusText: string;
} | undefined

Current vs. Expected behavior

CURRENT BEHAVIOR
When I log the error from the results of adding a passkey, it has a code property.

EXPECTED BEHAVIOR
The type of the error property in the results of adding a passkey should have a code property.

What version of Better Auth are you using?

1.3.4

Provide environment information

- OS: Windows 11
- Browser: Google Chrome

Which area(s) are affected? (Select all that apply)

Types

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { authAdapter } from "./firebase";
import { nextCookies } from "better-auth/next-js";
import { oneTap } from "better-auth/plugins";
import { headers } from "next/headers";
import { redirect } from "next/navigation";
import { cache } from "react";
import { siteInfo, siteLinks } from "@/config/site";
import { passkey } from "better-auth/plugins/passkey";

export const auth = betterAuth({
  database: authAdapter,
  plugins: [
    passkey({
      rpName: siteInfo.title,
      rpID: "better-auth-with-firebase",
      origin: process.env.BETTER_AUTH_URL,
      authenticatorSelection: {
        userVerification: "required",
        residentKey: "preferred",
      },
    }),
    oneTap({
      clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID as string,
      disableSignup: false,
    }),
    nextCookies(),
  ],
  emailAndPassword: {
    enabled: true,
    minPasswordLength: 12,
    autoSignIn: true,
  },
  account: {
    accountLinking: {
      enabled: true,
      allowDifferentEmails: false,
    },
  },
  socialProviders: {
    google: {
      clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    },
    github: {
      clientId: process.env.GITHUB_CLIENT_ID as string,
      clientSecret: process.env.GITHUB_CLIENT_SECRET as string,
    },
  },
  rateLimit: {
    storage: "database",
    modelName: "rateLimit",
    enabled: true,
  },
  session: {
    expiresIn: 60 * 60 * 24 * 7, // 7 days
    updateAge: 60 * 60 * 24, // 1 day (every 1 day the session expiration is updated)
    freshAge: 60 * 30, // 30 minutes (the session is fresh if created within the last 30 minutes)
    cookieCache: {
      enabled: true,
      maxAge: 15 * 60, // Cache duration in seconds
    },
  },
});

Additional context

I tried to review the code and it looks like some of the errors are handled manually and then an error is returned without the code. Will it be possible for us to add the code or will it break anything? I can create a pull request but I am not sure what impact it will ahve.

In my use case for example, the error code I wanted to handle was SESSION_NOT_FRESH and the message in the result.error was something like Session is not fresh. This is good but I think that as the library evolves, messages from errors might change so checking for specific messages or text strings like that is not the best but the code is more dependable since it will hardly change.

Originally created by @discoverlance-com on GitHub (Aug 8, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Create a backend 2. Use Passkey plugin ```ts export const auth = betterAuth({ ..., plugins: [ passkey({}), ], ``` 3. Add Passkey plugin to client auth ```ts export const authClient = createAuthClient({ ..., plugins: [ passkeyClient(), ] }) ``` 4. Call the addPasskey function on the auth client ```ts const result = await authClient.passkey.addPasskey({ name: values.passkey_name, authenticatorAttachment: "cross-platform", }); ``` 5. You will need to trigger some kind of failure, in my case, the session was not fresh. Log the `result.error` ```ts // the type of result.error error: { message?: string | undefined; status: number; statusText: string; } | undefined ``` ### Current vs. Expected behavior CURRENT BEHAVIOR When I log the error from the results of adding a passkey, it has a code property. EXPECTED BEHAVIOR The type of the error property in the results of adding a passkey should have a code property. ### What version of Better Auth are you using? 1.3.4 ### Provide environment information ```bash - OS: Windows 11 - Browser: Google Chrome ``` ### Which area(s) are affected? (Select all that apply) Types ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { authAdapter } from "./firebase"; import { nextCookies } from "better-auth/next-js"; import { oneTap } from "better-auth/plugins"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; import { cache } from "react"; import { siteInfo, siteLinks } from "@/config/site"; import { passkey } from "better-auth/plugins/passkey"; export const auth = betterAuth({ database: authAdapter, plugins: [ passkey({ rpName: siteInfo.title, rpID: "better-auth-with-firebase", origin: process.env.BETTER_AUTH_URL, authenticatorSelection: { userVerification: "required", residentKey: "preferred", }, }), oneTap({ clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID as string, disableSignup: false, }), nextCookies(), ], emailAndPassword: { enabled: true, minPasswordLength: 12, autoSignIn: true, }, account: { accountLinking: { enabled: true, allowDifferentEmails: false, }, }, socialProviders: { google: { clientId: process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, github: { clientId: process.env.GITHUB_CLIENT_ID as string, clientSecret: process.env.GITHUB_CLIENT_SECRET as string, }, }, rateLimit: { storage: "database", modelName: "rateLimit", enabled: true, }, session: { expiresIn: 60 * 60 * 24 * 7, // 7 days updateAge: 60 * 60 * 24, // 1 day (every 1 day the session expiration is updated) freshAge: 60 * 30, // 30 minutes (the session is fresh if created within the last 30 minutes) cookieCache: { enabled: true, maxAge: 15 * 60, // Cache duration in seconds }, }, }); ``` ### Additional context I tried to review the code and it looks like some of the errors are handled manually and then an error is returned without the code. Will it be possible for us to add the code or will it break anything? I can create a pull request but I am not sure what impact it will ahve. In my use case for example, the error code I wanted to handle was `SESSION_NOT_FRESH` and the message in the `result.error` was something like `Session is not fresh`. This is good but I think that as the library evolves, messages from errors might change so checking for specific messages or text strings like that is not the best but the code is more dependable since it will hardly change.
GiteaMirror added the enhancement label 2026-03-13 08:54:37 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Aug 8, 2025):

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

You're right—at runtime, passkey errors often include a code property (like SESSION_IS_NOT_FRESH), but the TypeScript types and some error construction paths in the passkey client don't reflect this. This happens because the client-side implementation manually builds error objects and omits the code, even when the underlying error (like WebAuthnError) has it. To make error.code reliably available and typed, you'd want to update both the error object construction in the client (e.g., always include code when present) and the error type definition to add code as an optional property. This matches patterns used elsewhere in the codebase and aligns with community expectations for robust error handling and i18n support example example.

This change is safe if you update both the implementation and the types together, but you should check for any user code that might rely on the old error shape. Testing in both TypeScript and runtime contexts will help catch any issues. If you make this change, it's a good idea to document it as a minor breaking change for anyone relying on the previous error type.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Aug 8, 2025): <!-- Greeting --> Hi @discoverlance-com! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. <!-- Answer --> You're right—at runtime, passkey errors often include a code property (like SESSION_IS_NOT_FRESH), but the TypeScript types and some error construction paths in the passkey client don't reflect this. This happens because the client-side implementation manually builds error objects and omits the code, even when the underlying error (like WebAuthnError) has it. To make error.code reliably available and typed, you'd want to update both the error object construction in the client (e.g., always include code when present) and the error type definition to add code as an optional property. This matches patterns used elsewhere in the codebase and aligns with community expectations for robust error handling and i18n support [example](https://github.com/better-auth/better-auth/issues/1297) [example](https://github.com/better-auth/better-auth/issues/1246). This change is safe if you update both the implementation and the types together, but you should check for any user code that might rely on the old error shape. Testing in both TypeScript and runtime contexts will help catch any issues. If you make this change, it's a good idea to document it as a minor breaking change for anyone relying on the previous error type. <!-- 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/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?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/3879)
Author
Owner

@discoverlance-com commented on GitHub (Aug 8, 2025):

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

You're right—at runtime, passkey errors often include a code property (like SESSION_IS_NOT_FRESH), but the TypeScript types and some error construction paths in the passkey client don't reflect this. This happens because the client-side implementation manually builds error objects and omits the code, even when the underlying error (like WebAuthnError) has it. To make error.code reliably available and typed, you'd want to update both the error object construction in the client (e.g., always include code when present) and the error type definition to add code as an optional property. This matches patterns used elsewhere in the codebase and aligns with community expectations for robust error handling and i18n support example example.

This change is safe if you update both the implementation and the types together, but you should check for any user code that might rely on the old error shape. Testing in both TypeScript and runtime contexts will help catch any issues. If you make this change, it's a good idea to document it as a minor breaking change for anyone relying on the previous error type.

To reply, just mention @dosu.

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

@dosu, so it looks like a breaking change then. Since it affects all client errors and not just the passkey error...

@discoverlance-com commented on GitHub (Aug 8, 2025): > Hi [@discoverlance-com](https://github.com/discoverlance-com)! I'm [Dosu](https://go.dosu.dev/dosubot) and I’m helping the better-auth team. > > You're right—at runtime, passkey errors often include a code property (like SESSION_IS_NOT_FRESH), but the TypeScript types and some error construction paths in the passkey client don't reflect this. This happens because the client-side implementation manually builds error objects and omits the code, even when the underlying error (like WebAuthnError) has it. To make error.code reliably available and typed, you'd want to update both the error object construction in the client (e.g., always include code when present) and the error type definition to add code as an optional property. This matches patterns used elsewhere in the codebase and aligns with community expectations for robust error handling and i18n support [example](https://github.com/better-auth/better-auth/issues/1297) [example](https://github.com/better-auth/better-auth/issues/1246). > > This change is safe if you update both the implementation and the types together, but you should check for any user code that might rely on the old error shape. Testing in both TypeScript and runtime contexts will help catch any issues. If you make this change, it's a good idea to document it as a minor breaking change for anyone relying on the previous error type. > > _To reply, just mention [@dosu](https://go.dosu.dev/dosubot)._ > > How did I do? [Good](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/05edacf9-1d57-4831-87c5-8ea2f3712203?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/3879) @dosu, so it looks like a breaking change then. Since it affects all client errors and not just the passkey error...
Author
Owner

@dosubot[bot] commented on GitHub (Aug 8, 2025):

Yes, updating error types to consistently include a code property is a breaking change for all client errors—not just passkey errors—because it changes the expected shape of error objects in TypeScript and at runtime. This impacts any user code that relies on the previous error structure, so you’ll need to update both the error construction logic (to always include code when available) and the type definitions across all plugins and client actions for consistency example.

Best practice is to refactor error handling everywhere errors are constructed, update types to reflect the new shape, and test both TypeScript and runtime error handling to catch regressions. Since there’s no official migration guide for this kind of change, document it as a breaking change in release notes and plugin docs. A migration guide should show before/after error shapes, upgrade steps, and how to update user code that checks error properties. This approach will make error handling more robust and predictable for everyone using better-auth example.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Aug 8, 2025): <!-- Answer --> Yes, updating error types to consistently include a `code` property is a breaking change for all client errors—not just passkey errors—because it changes the expected shape of error objects in TypeScript and at runtime. This impacts any user code that relies on the previous error structure, so you’ll need to update both the error construction logic (to always include `code` when available) and the type definitions across all plugins and client actions for consistency [example](https://github.com/better-auth/better-auth/issues/1297). Best practice is to refactor error handling everywhere errors are constructed, update types to reflect the new shape, and test both TypeScript and runtime error handling to catch regressions. Since there’s no official migration guide for this kind of change, document it as a breaking change in release notes and plugin docs. A migration guide should show before/after error shapes, upgrade steps, and how to update user code that checks error properties. This approach will make error handling more robust and predictable for everyone using better-auth [example](https://github.com/better-auth/better-auth/issues/1246). <!-- 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/4ef79185-7a05-4a33-ac52-8b9c5b16b59d?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/4ef79185-7a05-4a33-ac52-8b9c5b16b59d?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/4ef79185-7a05-4a33-ac52-8b9c5b16b59d?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/4ef79185-7a05-4a33-ac52-8b9c5b16b59d?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/4ef79185-7a05-4a33-ac52-8b9c5b16b59d?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/4ef79185-7a05-4a33-ac52-8b9c5b16b59d?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/4ef79185-7a05-4a33-ac52-8b9c5b16b59d?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/3879)
Author
Owner

@discoverlance-com commented on GitHub (Aug 8, 2025):

@dosu, I can see though that for client actions like authClient.updateUser, the updateUser client action, it does have the code property (as optional field) in the error returned so it seems it's not all client errors that don't return a code property, it might be isolated to some parts like the passkey

@discoverlance-com commented on GitHub (Aug 8, 2025): @dosu, I can see though that for client actions like `authClient.updateUser`, the `updateUser` client action, it does have the `code` property (as optional field) in the error returned so it seems it's not all client errors that don't return a `code` property, it might be isolated to some parts like the passkey
Author
Owner

@dosubot[bot] commented on GitHub (Aug 8, 2025):

You're right—the omission of the code property is isolated to certain client actions like the passkey plugin. Actions like updateUser consistently include code in their error objects because they use APIError, which sets code at both the type and runtime level. The inconsistency comes from some plugins (like passkey) manually constructing error objects and omitting code, while others propagate errors from the backend where code is always present see code see passkey client issue #2052.

To fix this, update the passkey client to always include code when present and adjust the TypeScript types. This will make error handling more predictable and robust across all client actions.

To reply, just mention @dosu.


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

@dosubot[bot] commented on GitHub (Aug 8, 2025): <!-- Answer --> You're right—the omission of the code property is isolated to certain client actions like the passkey plugin. Actions like updateUser consistently include code in their error objects because they use APIError, which sets code at both the type and runtime level. The inconsistency comes from some plugins (like passkey) manually constructing error objects and omitting code, while others propagate errors from the backend where code is always present [see code](https://github.com/better-auth/better-auth/blob/0fe17704048bca8d52b547eb57a53ca89e84b78c/packages/better-auth/src/api/routes/update-user.ts) [see passkey client](https://github.com/better-auth/better-auth/blob/0fe17704048bca8d52b547eb57a53ca89e84b78c/packages/better-auth/src/plugins/passkey/client.ts) [issue #2052](https://github.com/better-auth/better-auth/issues/2052). To fix this, update the passkey client to always include code when present and adjust the TypeScript types. This will make error handling more predictable and robust across all client actions. <!-- 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/ba666bcb-ac3f-47af-af21-944e3118a852?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/ba666bcb-ac3f-47af-af21-944e3118a852?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/ba666bcb-ac3f-47af-af21-944e3118a852?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/ba666bcb-ac3f-47af-af21-944e3118a852?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/ba666bcb-ac3f-47af-af21-944e3118a852?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/ba666bcb-ac3f-47af-af21-944e3118a852?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/ba666bcb-ac3f-47af-af21-944e3118a852?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/3879)
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1665