Duplicate error path in error URL #2818

Closed
opened 2026-03-13 10:22:02 -05:00 by GiteaMirror · 2 comments
Owner

Originally created by @bojanbass on GitHub (Feb 4, 2026).

Originally assigned to: @bytaesu on GitHub.

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

Incorrectly configure SSO provider so it errors out. Check the redirect URL - it should point to the generic error page, but renders not found url.

Current vs. Expected behavior

Redirect to error URL contains 2 error paths and renders not found page:

api/auth/error/error?error=invalid_provider&error_description=token_response_not_found

It should only contain one error path and display the error page:

api/auth/error/error?error=invalid_provider&error_description=token_response_not_found


<img width="705" height="382" alt="Image" src="https://github.com/user-attachments/assets/8a4d74ab-9532-4e53-99d7-a131b50ebaaa" />



### What version of Better Auth are you using?

1.4.3

### System info

```bash
{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000",
    "release": "25.2.0",
    "cpuCount": 10,
    "cpuModel": "Apple M1 Max",
    "totalMemory": "32.00 GB",
    "freeMemory": "0.20 GB"
  },
  "node": {
    "version": "v24.13.0",
    "env": "development"
  },
  "packageManager": {
    "name": "yarn",
    "version": "3.6.4"
  },
  "frameworks": [
    {
      "name": "next",
      "version": "16.0.10"
    },
    {
      "name": "react",
      "version": "19.2.3"
    },
    {
      "name": "vue",
      "version": "^2.6.12"
    }
  ],
  "databases": [
    {
      "name": "pg",
      "version": "^8.16.3"
    },
    {
      "name": "drizzle",
      "version": "^0.44.7"
    }
  ],
  "betterAuth": {
    "version": "1.4.3",
    "config": {
      "experimental": {
        "joins": true
      },
      "appName": "Test",
      "emailAndPassword": {
        "enabled": true
      },
      "trustedOrigins": [
        "localhost"
      ],
      "advanced": {},
      "plugins": [
        {
          "name": "api-key",
          "config": {
            "id": "api-key",
            "$ERROR_CODES": {
              "INVALID_METADATA_TYPE": "metadata must be an object or undefined",
              "REFILL_AMOUNT_AND_INTERVAL_REQUIRED": "refillAmount is required when refillInterval is provided",
              "REFILL_INTERVAL_AND_AMOUNT_REQUIRED": "refillInterval is required when refillAmount is provided",
              "USER_BANNED": "User is banned",
              "UNAUTHORIZED_SESSION": "Unauthorized or invalid session",
              "KEY_NOT_FOUND": "API Key not found",
              "KEY_DISABLED": "API Key is disabled",
              "KEY_EXPIRED": "API Key has expired",
              "USAGE_EXCEEDED": "API Key has reached its usage limit",
              "KEY_NOT_RECOVERABLE": "API Key is not recoverable",
              "EXPIRES_IN_IS_TOO_SMALL": "The expiresIn is smaller than the predefined minimum value.",
              "EXPIRES_IN_IS_TOO_LARGE": "The expiresIn is larger than the predefined maximum value.",
              "INVALID_REMAINING": "The remaining count is either too large or too small.",
              "INVALID_PREFIX_LENGTH": "The prefix length is either too large or too small.",
              "INVALID_NAME_LENGTH": "The name length is either too large or too small.",
              "METADATA_DISABLED": "Metadata is disabled.",
              "RATE_LIMIT_EXCEEDED": "Rate limit exceeded.",
              "NO_VALUES_TO_UPDATE": "No values to update.",
              "KEY_DISABLED_EXPIRATION": "Custom key expiration values are disabled.",
              "INVALID_API_KEY": "Invalid API key.",
              "INVALID_USER_ID_FROM_API_KEY": "The user id from the API key is invalid.",
              "INVALID_API_KEY_GETTER_RETURN_TYPE": "API Key getter returned an invalid key type. Expected string.",
              "SERVER_ONLY_PROPERTY": "The property you're trying to set can only be set from the server auth instance only.",
              "FAILED_TO_UPDATE_API_KEY": "Failed to update API key",
              "NAME_REQUIRED": "API Key name is required."
            },
            "hooks": {
              "before": [
                {}
              ]
            },
            "endpoints": {},
            "schema": {
              "apikey": {
                "fields": {
                  "name": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "start": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "prefix": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "key": {
                    "type": "string",
                    "required": true,
                    "input": false,
                    "index": true
                  },
                  "userId": {
                    "type": "string",
                    "references": {
                      "model": "user",
                      "field": "id",
                      "onDelete": "cascade"
                    },
                    "required": true,
                    "input": false,
                    "index": true
                  },
                  "refillInterval": {
                    "type": "number",
                    "required": false,
                    "input": false
                  },
                  "refillAmount": {
                    "type": "number",
                    "required": false,
                    "input": false
                  },
                  "lastRefillAt": {
                    "type": "date",
                    "required": false,
                    "input": false
                  },
                  "enabled": {
                    "type": "boolean",
                    "required": false,
                    "input": false,
                    "defaultValue": true
                  },
                  "rateLimitEnabled": {
                    "type": "boolean",
                    "required": false,
                    "input": false,
                    "defaultValue": true
                  },
                  "rateLimitTimeWindow": {
                    "type": "number",
                    "required": false,
                    "input": false,
                    "defaultValue": 60000
                  },
                  "rateLimitMax": {
                    "type": "number",
                    "required": false,
                    "input": false,
                    "defaultValue": 100
                  },
                  "requestCount": {
                    "type": "number",
                    "required": false,
                    "input": false,
                    "defaultValue": 0
                  },
                  "remaining": {
                    "type": "number",
                    "required": false,
                    "input": false
                  },
                  "lastRequest": {
                    "type": "date",
                    "required": false,
                    "input": false
                  },
                  "expiresAt": {
                    "type": "date",
                    "required": false,
                    "input": false
                  },
                  "createdAt": {
                    "type": "date",
                    "required": true,
                    "input": false
                  },
                  "updatedAt": {
                    "type": "date",
                    "required": true,
                    "input": false
                  },
                  "permissions": {
                    "type": "string",
                    "required": false,
                    "input": false
                  },
                  "metadata": {
                    "type": "string",
                    "required": false,
                    "input": true,
                    "transform": {}
                  }
                }
              }
            }
          }
        },
        {
          "name": "sso",
          "config": {
            "id": "sso",
            "endpoints": {},
            "schema": {
              "ssoProvider": {
                "modelName": "ssoProvider",
                "fields": {
                  "issuer": {
                    "type": "string",
                    "required": true,
                    "fieldName": "issuer"
                  },
                  "oidcConfig": {
                    "type": "string",
                    "required": false,
                    "fieldName": "oidcConfig"
                  },
                  "samlConfig": {
                    "type": "string",
                    "required": false,
                    "fieldName": "samlConfig"
                  },
                  "userId": {
                    "type": "string",
                    "references": {
                      "model": "user",
                      "field": "id"
                    },
                    "fieldName": "userId"
                  },
                  "providerId": {
                    "type": "string",
                    "required": true,
                    "unique": true,
                    "fieldName": "providerId"
                  },
                  "organizationId": {
                    "type": "string",
                    "required": false,
                    "fieldName": "organizationId"
                  },
                  "domain": {
                    "type": "string",
                    "required": true,
                    "fieldName": "domain"
                  }
                }
              }
            }
          }
        }
      ]
    }
  }
}

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth"
export const auth = betterAuth({
  plugins: [
      sso({
        defaultSSO: [
          {
            providerId: 'customProvider',
            domain: ssoOptions.domain,
            oidcConfig: {
              issuer: ssoOptions.issuerUrl,
              clientId: ssoOptions.clientId,
              clientSecret: ssoOptions.clientSecret,
              authorizationEndpoint: ssoOptions.authorizationEndpoint,
              tokenEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/token/`,
              jwksEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/.well-known/jwks.json`,
              userInfoEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/userinfo/`,
              discoveryEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/.well-known/openid-configuration`,
              scopes: ['openid', 'profile', 'email'],
              pkce: true,
              mapping: {
                extraFields: ssoExtraFields,
              },
            },
          },
        ],
        provisionUser: ssoOptions.provisionUser,
      }),
]
});

Additional context

When onAPIError.errorURL is not set, it defaults to ${baseUrl}/error:
https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1392

But later when this error url is used, it is not used consistently. Sometimes an additional error is added to the path:
https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1496

Sometimes not:
https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1518

Originally created by @bojanbass on GitHub (Feb 4, 2026). Originally assigned to: @bytaesu on GitHub. ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce Incorrectly configure SSO provider so it errors out. Check the redirect URL - it should point to the generic error page, but renders not found url. ### Current vs. Expected behavior Redirect to error URL contains 2 `error` paths and renders not found page: ``` api/auth/error/error?error=invalid_provider&error_description=token_response_not_found ``` It should only contain one `error` path and display the error page: ``` ``` api/auth/error/error?error=invalid_provider&error_description=token_response_not_found ``` <img width="705" height="382" alt="Image" src="https://github.com/user-attachments/assets/8a4d74ab-9532-4e53-99d7-a131b50ebaaa" /> ### What version of Better Auth are you using? 1.4.3 ### System info ```bash { "system": { "platform": "darwin", "arch": "arm64", "version": "Darwin Kernel Version 25.2.0: Tue Nov 18 21:09:40 PST 2025; root:xnu-12377.61.12~1/RELEASE_ARM64_T6000", "release": "25.2.0", "cpuCount": 10, "cpuModel": "Apple M1 Max", "totalMemory": "32.00 GB", "freeMemory": "0.20 GB" }, "node": { "version": "v24.13.0", "env": "development" }, "packageManager": { "name": "yarn", "version": "3.6.4" }, "frameworks": [ { "name": "next", "version": "16.0.10" }, { "name": "react", "version": "19.2.3" }, { "name": "vue", "version": "^2.6.12" } ], "databases": [ { "name": "pg", "version": "^8.16.3" }, { "name": "drizzle", "version": "^0.44.7" } ], "betterAuth": { "version": "1.4.3", "config": { "experimental": { "joins": true }, "appName": "Test", "emailAndPassword": { "enabled": true }, "trustedOrigins": [ "localhost" ], "advanced": {}, "plugins": [ { "name": "api-key", "config": { "id": "api-key", "$ERROR_CODES": { "INVALID_METADATA_TYPE": "metadata must be an object or undefined", "REFILL_AMOUNT_AND_INTERVAL_REQUIRED": "refillAmount is required when refillInterval is provided", "REFILL_INTERVAL_AND_AMOUNT_REQUIRED": "refillInterval is required when refillAmount is provided", "USER_BANNED": "User is banned", "UNAUTHORIZED_SESSION": "Unauthorized or invalid session", "KEY_NOT_FOUND": "API Key not found", "KEY_DISABLED": "API Key is disabled", "KEY_EXPIRED": "API Key has expired", "USAGE_EXCEEDED": "API Key has reached its usage limit", "KEY_NOT_RECOVERABLE": "API Key is not recoverable", "EXPIRES_IN_IS_TOO_SMALL": "The expiresIn is smaller than the predefined minimum value.", "EXPIRES_IN_IS_TOO_LARGE": "The expiresIn is larger than the predefined maximum value.", "INVALID_REMAINING": "The remaining count is either too large or too small.", "INVALID_PREFIX_LENGTH": "The prefix length is either too large or too small.", "INVALID_NAME_LENGTH": "The name length is either too large or too small.", "METADATA_DISABLED": "Metadata is disabled.", "RATE_LIMIT_EXCEEDED": "Rate limit exceeded.", "NO_VALUES_TO_UPDATE": "No values to update.", "KEY_DISABLED_EXPIRATION": "Custom key expiration values are disabled.", "INVALID_API_KEY": "Invalid API key.", "INVALID_USER_ID_FROM_API_KEY": "The user id from the API key is invalid.", "INVALID_API_KEY_GETTER_RETURN_TYPE": "API Key getter returned an invalid key type. Expected string.", "SERVER_ONLY_PROPERTY": "The property you're trying to set can only be set from the server auth instance only.", "FAILED_TO_UPDATE_API_KEY": "Failed to update API key", "NAME_REQUIRED": "API Key name is required." }, "hooks": { "before": [ {} ] }, "endpoints": {}, "schema": { "apikey": { "fields": { "name": { "type": "string", "required": false, "input": false }, "start": { "type": "string", "required": false, "input": false }, "prefix": { "type": "string", "required": false, "input": false }, "key": { "type": "string", "required": true, "input": false, "index": true }, "userId": { "type": "string", "references": { "model": "user", "field": "id", "onDelete": "cascade" }, "required": true, "input": false, "index": true }, "refillInterval": { "type": "number", "required": false, "input": false }, "refillAmount": { "type": "number", "required": false, "input": false }, "lastRefillAt": { "type": "date", "required": false, "input": false }, "enabled": { "type": "boolean", "required": false, "input": false, "defaultValue": true }, "rateLimitEnabled": { "type": "boolean", "required": false, "input": false, "defaultValue": true }, "rateLimitTimeWindow": { "type": "number", "required": false, "input": false, "defaultValue": 60000 }, "rateLimitMax": { "type": "number", "required": false, "input": false, "defaultValue": 100 }, "requestCount": { "type": "number", "required": false, "input": false, "defaultValue": 0 }, "remaining": { "type": "number", "required": false, "input": false }, "lastRequest": { "type": "date", "required": false, "input": false }, "expiresAt": { "type": "date", "required": false, "input": false }, "createdAt": { "type": "date", "required": true, "input": false }, "updatedAt": { "type": "date", "required": true, "input": false }, "permissions": { "type": "string", "required": false, "input": false }, "metadata": { "type": "string", "required": false, "input": true, "transform": {} } } } } } }, { "name": "sso", "config": { "id": "sso", "endpoints": {}, "schema": { "ssoProvider": { "modelName": "ssoProvider", "fields": { "issuer": { "type": "string", "required": true, "fieldName": "issuer" }, "oidcConfig": { "type": "string", "required": false, "fieldName": "oidcConfig" }, "samlConfig": { "type": "string", "required": false, "fieldName": "samlConfig" }, "userId": { "type": "string", "references": { "model": "user", "field": "id" }, "fieldName": "userId" }, "providerId": { "type": "string", "required": true, "unique": true, "fieldName": "providerId" }, "organizationId": { "type": "string", "required": false, "fieldName": "organizationId" }, "domain": { "type": "string", "required": true, "fieldName": "domain" } } } } } } ] } } } ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth" export const auth = betterAuth({ plugins: [ sso({ defaultSSO: [ { providerId: 'customProvider', domain: ssoOptions.domain, oidcConfig: { issuer: ssoOptions.issuerUrl, clientId: ssoOptions.clientId, clientSecret: ssoOptions.clientSecret, authorizationEndpoint: ssoOptions.authorizationEndpoint, tokenEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/token/`, jwksEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/.well-known/jwks.json`, userInfoEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/userinfo/`, discoveryEndpoint: `${ssoOptions.authorizationEndpoint}/oauth/.well-known/openid-configuration`, scopes: ['openid', 'profile', 'email'], pkce: true, mapping: { extraFields: ssoExtraFields, }, }, }, ], provisionUser: ssoOptions.provisionUser, }), ] }); ``` ### Additional context When `onAPIError.errorURL` is not set, it defaults to `${baseUrl}/error`: https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1392 But later when this error url is used, it is not used consistently. Sometimes an additional `error` is added to the path: https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1496 Sometimes not: https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1518
GiteaMirror added the bug label 2026-03-13 10:22:02 -05:00
Author
Owner

@dosubot[bot] commented on GitHub (Feb 4, 2026):

This is a known bug in Better Auth v1.4.3. The duplicate /error/error in the SSO error URL happens because the code sometimes appends /error to an errorURL that already ends with /error, leading to URLs like /api/auth/error/error?... and a 404 instead of the error page. This comes from inconsistent error URL construction in the SSO backend logic—sometimes it uses ${errorURL}/error?..., sometimes just ${errorURL}?... depending on the error branch. You can see this in the code around these lines, here, and here.

This is fixed in newer versions (after January 2026) via PR #6675 and PR #5721, which normalize error URL handling and prevent duplicate segments.

If you can't upgrade yet, you can work around this by explicitly setting onAPIError.errorURL in your Better Auth config to your frontend error page (e.g., /auth/error), or by using middleware to intercept /api/auth/error and redirect to your desired error page. This avoids the backend appending another /error and prevents the 404.

Your config and SSO setup are not at fault—this is a backend bug that has since been fixed.

To reply, just mention @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other

Chat with Dosu Open in Cursor Join Discord Share on X

@dosubot[bot] commented on GitHub (Feb 4, 2026): <!-- Answer --> This is a known bug in Better Auth v1.4.3. The duplicate `/error/error` in the SSO error URL happens because the code sometimes appends `/error` to an errorURL that already ends with `/error`, leading to URLs like `/api/auth/error/error?...` and a 404 instead of the error page. This comes from inconsistent error URL construction in the SSO backend logic—sometimes it uses `${errorURL}/error?...`, sometimes just `${errorURL}?...` depending on the error branch. You can see this in the code around [these lines](https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1392), [here](https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1496), and [here](https://github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts#L1518). This is fixed in newer versions (after January 2026) via [PR #6675](https://github.com/better-auth/better-auth/pull/6675) and [PR #5721](https://github.com/better-auth/better-auth/pull/5721), which normalize error URL handling and prevent duplicate segments. If you can't upgrade yet, you can work around this by explicitly setting `onAPIError.errorURL` in your Better Auth config to your frontend error page (e.g., `/auth/error`), or by using middleware to intercept `/api/auth/error` and redirect to your desired error page. This avoids the backend appending another `/error` and prevents the 404. Your config and SSO setup are not at fault—this is a backend bug that has since been fixed. <!-- 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/5e1a96f1-cc21-49e5-908a-8c8e0e66b3e4?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/5e1a96f1-cc21-49e5-908a-8c8e0e66b3e4?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/5e1a96f1-cc21-49e5-908a-8c8e0e66b3e4?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/5e1a96f1-cc21-49e5-908a-8c8e0e66b3e4?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/5e1a96f1-cc21-49e5-908a-8c8e0e66b3e4?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/5e1a96f1-cc21-49e5-908a-8c8e0e66b3e4?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/5e1a96f1-cc21-49e5-908a-8c8e0e66b3e4?feedback_type=other)</sup> [![Chat with Dosu](https://dosu.dev/dosu-chat-badge.svg)](https://app.dosu.dev/cdda13d9-dd27-4d31-b09a-5d8bec92de21/ask?utm_source=github)&nbsp;[![Open in Cursor](https://dosu.dev/dosu-in-cursor.svg)](https://cursor.com/link/prompt?text=This%20is%20a%20known%20bug%20in%20Better%20Auth%20v1.4.3.%20The%20duplicate%20%60/error/error%60%20in%20the%20SSO%20error%20URL%20happens%20because%20the%20code%20sometimes%20appends%20%60/error%60%20to%20an%20errorURL%20that%20already%20ends%20with%20%60/error%60%2C%20leading%20to%20URLs%20like%20%60/api/auth/error/error%3F...%60%20and%20a%20404%20instead%20of%20the%20error%20page.%20This%20comes%20from%20inconsistent%20error%20URL%20construction%20in%20the%20SSO%20backend%20logic%E2%80%94sometimes%20it%20uses%20%60%24%7BerrorURL%7D/error%3F...%60%2C%20sometimes%20just%20%60%24%7BerrorURL%7D%3F...%60%20depending%20on%20the%20error%20branch.%20You%20can%20see%20this%20in%20the%20code%20around%20%5Bthese%20lines%5D%28https%3A//github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts%23L1392%29%2C%20%5Bhere%5D%28https%3A//github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts%23L1496%29%2C%20and%20%5Bhere%5D%28https%3A//github.com/better-auth/better-auth/blob/main/packages/sso/src/routes/sso.ts%23L1518%29.%0A%0AThis%20is%20fixed%20in%20newer%20versions%20%28after%20January%202026%29%20via%20%5BPR%20%236675%5D%28https%3A//github.com/better-auth/better-auth/pull/6675%29%20and%20%5BPR%20%235721%5D%28https%3A//github.com/better-auth/better-auth/pull/5721%29%2C%20which%20normalize%20error%20URL%20handling%20and%20prevent%20duplicate%20segments.%0A%0AIf%20you%20can%27t%20upgrade%20yet%2C%20you%20can%20work%20around%20this%20by%20explicitly%20setting%20%60onAPIError.errorURL%60%20in%20your%20Better%20Auth%20config%20to%20your%20frontend%20error%20page%20%28e.g.%2C%20%60/auth/error%60%29%2C%20or%20by%20using%20middleware%20to%20intercept%20%60/api/auth/error%60%20and%20redirect%20to%20your%20desired%20error%20page.%20This%20avoids%20the%20backend%20appending%20another%20%60/error%60%20and%20prevents%20the%20404.%0A%0AYour%20config%20and%20SSO%20setup%20are%20not%20at%20fault%E2%80%94this%20is%20a%20backend%20bug%20that%20has%20since%20been%20fixed.)&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/7788)
Author
Owner

@bytaesu commented on GitHub (Feb 4, 2026):

Hi @bojanbass, thanks for letting us know! I'm looking into this.

@bytaesu commented on GitHub (Feb 4, 2026): Hi @bojanbass, thanks for letting us know! I'm looking into this.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#2818