error thrown in better auth plugin is not captured by better-auth configured logger

I’m trying to use Better Auth in a Fastify application Below is the part of my configuration: const betterAuthOptions: BetterAuthOptions = { // Better Auth should delegate its logs to Fastify logger: { disabled: false, level: server.log.level as 'info', log(level, message, ...args) { // Forward everything to Fastify’s pino instance server.log[level]({ ...args }, message); }, }, plugins: [ emailOTP({ async sendVerificationOTP({ email, otp, type }) { server.log.debug({ email, otp, type }, 'emailOTP'); // ➜ This call is where the error occurs if (type === 'sign-in') { await emailer.sendOTP({ email, otp }); } }, generateOTP: genOtp, disableSignUp: false, expiresIn: 300, // 5 minutes sendVerificationOnSignUp: false, }), ], }; Problem When emailer.sendOTP throws the error: Isn’t caught by the Better Auth logger I configured above. Doesn’t appear in Fastify’s setErrorHandler. Instead shows up only as an unhandled error in the console. What I expected Because sendVerificationOTP lives inside the Better Auth plugin, I assumed any exceptions thrown there would bubble up to Better Auth’s internal error-handling path and be logged by the custom logger I passed in. What actually happens SERVER_ERROR: TypeError: fetch failed at node:internal/deps/undici/undici:13392:13
Fastify’s structured logs (Pino) Better Auth’s logger callback above Questions Is this the intended behaviour, i.e. does Better Auth expect you to wrap sendVerificationOTP in a try/catch? If not, is there a recommended pattern for ensuring errors inside plugin callbacks are routed through Better Auth’s logger (or Fastify’s error handler)? Has anyone run into this and found a workaround other than manually wrapping emailer.sendOTP in a try/catch and logging it yourself? Any guidance or examples would be greatly appreciated.
2 Replies
javid
javidOP3mo ago
@Better Auth , i need to modify the response structure how to do that
Better Auth
Better Auth3mo ago
You can modify the response structure by using an “after hook.” After hooks run once an endpoint has executed, letting you intercept and adjust the response before it’s sent to the client [1]. For instance, you can use createAuthMiddleware to define an after hook that returns a modified JSON response with your desired structure. Here's a simple example: ------------------------------------------------- import { betterAuth } from "better-auth"; import { createAuthMiddleware } from "better-auth/api"; export const auth = betterAuth({ hooks: { after: createAuthMiddleware(async (ctx) => { // Check for a specific endpoint if needed if (ctx.path === "/some-endpoint") { const originalResponse = { ...ctx.context.response }; // Modify the response structure as required return ctx.json({ status: "success", data: originalResponse, extra: "Your custom data here", }); } // For other endpoints, continue without changes }), }, }); ------------------------------------------------- This way, you can tailor the output of any endpoint to your specific needs. Let me know if you have any questions about this approach! Sources: 1. Hooks | Better Auth
Hooks | Better Auth
Better Auth Hooks let you customize BetterAuth's behavior

Did you find this page helpful?