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.
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
@Better Auth , i need to modify the response structure how to do that
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