Custom error page (errorURL in onAPIError) not used for all errors #1154

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

Originally created by @ErikPetersenDev on GitHub (May 3, 2025).

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

This occurs with several issues, but this is one consistent way of doing it:

  1. Set a custom error page in Better Auth options
  2. Log in with Google.
  3. Hit the back button to go back to the Google sign-in screen.
  4. Choose the same account again.

Default Better Auth error page is shown.

Current vs. Expected behavior

Expected: If a custom error page is defined it should be used for all API errors.

What version of Better Auth are you using?

1.2.7

Provide environment information

- OS: MacOS
- Browser: Chrome

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

Backend

Auth config (if applicable)

relevant portion:


  onAPIError: {
    throw: false,
    onError: (error) => {
      console.error("Auth error:", error);
    },
    errorURL: "/auth-error",
  },

Additional context

I found code in node_modules for the specific error I was generating (see reproduction steps above). The code has the error url hardcoded to the base Better Auth error page instead of honoring the custom URL in auth config.

From the code - see it's redirecting to baseURL/error:
2a246aeb87/packages/better-auth/src/oauth2/state.ts (L63)

  if (!data) {
    c.context.logger.error("State Mismatch. Verification not found", {
      state
    });
    throw c.redirect(
      `${c.context.baseURL}/error?error=please_restart_the_process`
    );
  }

Here's an example from elsewhere in the codebase that does honor the override. It checks options for "onAPIError"

          const defaultErrorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`;
          if (ctx.query.error || !ctx.query.code) {
            throw ctx.redirect(
              `${defaultErrorURL}?error=${ctx.query.error || "oAuth_code_missing"}&error_description=${ctx.query.error_description}`
            );
          }
Originally created by @ErikPetersenDev on GitHub (May 3, 2025). ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce This occurs with several issues, but this is one consistent way of doing it: 1. Set a custom error page in Better Auth options 2. Log in with Google. 3. Hit the back button to go back to the Google sign-in screen. 4. Choose the same account again. Default Better Auth error page is shown. ### Current vs. Expected behavior Expected: If a custom error page is defined it should be used for all API errors. ### What version of Better Auth are you using? 1.2.7 ### Provide environment information ```bash - OS: MacOS - Browser: Chrome ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript relevant portion: onAPIError: { throw: false, onError: (error) => { console.error("Auth error:", error); }, errorURL: "/auth-error", }, ``` ### Additional context I found code in `node_modules` for the specific error I was generating (see reproduction steps above). The code has the error url hardcoded to the base Better Auth error page instead of honoring the custom URL in auth config. From the code - see it's redirecting to baseURL/error: https://github.com/better-auth/better-auth/blob/2a246aeb8735246afb405f705cc5e535f330e303/packages/better-auth/src/oauth2/state.ts#L63 ``` if (!data) { c.context.logger.error("State Mismatch. Verification not found", { state }); throw c.redirect( `${c.context.baseURL}/error?error=please_restart_the_process` ); } ``` Here's an example from elsewhere in the codebase that does honor the override. It checks options for "onAPIError" ``` const defaultErrorURL = ctx.context.options.onAPIError?.errorURL || `${ctx.context.baseURL}/error`; if (ctx.query.error || !ctx.query.code) { throw ctx.redirect( `${defaultErrorURL}?error=${ctx.query.error || "oAuth_code_missing"}&error_description=${ctx.query.error_description}` ); } ```
Author
Owner

@ErikPetersenDev commented on GitHub (May 3, 2025):

Helpful search to find places in the code with this bug: https://github.com/search?q=repo%3Abetter-auth%2Fbetter-auth%20baseURL%7D%2Ferror%3F&type=code

@ErikPetersenDev commented on GitHub (May 3, 2025): Helpful search to find places in the code with this bug: https://github.com/search?q=repo%3Abetter-auth%2Fbetter-auth%20baseURL%7D%2Ferror%3F&type=code
Author
Owner

@coopbri commented on GitHub (May 4, 2025):

Thanks for making this issue. More discussion in Discord (as you saw, just linking this for others): https://discord.com/channels/1288403910284935179/1364982370402570394

@coopbri commented on GitHub (May 4, 2025): Thanks for making this issue. More discussion in Discord (as you saw, just linking this for others): https://discord.com/channels/1288403910284935179/1364982370402570394
Author
Owner

@Sharlmagne commented on GitHub (May 9, 2025):

I hope this gets fixed, I am having the same issue after simulating an error in my custom oauth2 plugin.

When the oauth2 fails it redirects to the default "/auth/error".

This does not work:

onAPIError: {
    errorURL: `${CLIENT_URL}/auth/error`,
  },
@Sharlmagne commented on GitHub (May 9, 2025): I hope this gets fixed, I am having the same issue after simulating an error in my custom oauth2 plugin. When the oauth2 fails it redirects to the default "/auth/error". This does not work: ``` onAPIError: { errorURL: `${CLIENT_URL}/auth/error`, }, ```
Author
Owner

@FalconiZzare commented on GitHub (Jun 15, 2025):

This is very useful for projects that use separate backend. Any estimate when it will be merged?

@FalconiZzare commented on GitHub (Jun 15, 2025): This is very useful for projects that use separate backend. Any estimate when it will be merged?
Author
Owner

@l0gicgate commented on GitHub (Jun 18, 2025):

For people looking for a workaround until this fix is released. If you are using Next.JS you can use the middleware to redirect to your page.

middleware.ts

import { getSessionCookie } from 'better-auth/cookies';
import { NextRequest, NextResponse } from 'next/server';

const middleware = (req: NextRequest): Response | void => {
  const sessionCookie = getSessionCookie(req);

  if (req.nextUrl.pathname === '/api/auth/error') {
    let code = '';
    if (req.nextUrl.searchParams.has('error')) {
      code = req.nextUrl.searchParams.get('error') ?? '';
    }

    // will redirect to /error?code=<code>
    const newUrl = new URL(
      `/error${code ? `?code=${encodeURIComponent(code)}` : ''}`,
      req.nextUrl.origin,
    );

    return Response.redirect(newUrl);
  }

  // ...

  return NextResponse.next();
};

export default middleware;

export const config = {
  matcher: [
    // ...
    '/api/auth/error',
  ],
};
@l0gicgate commented on GitHub (Jun 18, 2025): For people looking for a workaround until this fix is released. If you are using Next.JS you can use the middleware to redirect to your page. **middleware.ts** ```ts import { getSessionCookie } from 'better-auth/cookies'; import { NextRequest, NextResponse } from 'next/server'; const middleware = (req: NextRequest): Response | void => { const sessionCookie = getSessionCookie(req); if (req.nextUrl.pathname === '/api/auth/error') { let code = ''; if (req.nextUrl.searchParams.has('error')) { code = req.nextUrl.searchParams.get('error') ?? ''; } // will redirect to /error?code=<code> const newUrl = new URL( `/error${code ? `?code=${encodeURIComponent(code)}` : ''}`, req.nextUrl.origin, ); return Response.redirect(newUrl); } // ... return NextResponse.next(); }; export default middleware; export const config = { matcher: [ // ... '/api/auth/error', ], };
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#1154