BA
Better Auth•4d ago
shadow

Email Verification required and Captcha return the same error code

Hey! 👋 I think this might be a bug — or at least a DX issue — because the current implementation doesn’t let us differentiate between different failure cases. Here’s the relevant code:
signIn.email({
email: values.email,
password: values.password,
callbackURL: "/dashboard",
fetchOptions: {
headers: {
"x-captcha-response": turnstileToken
},
onResponse: () => {
setLoading(false);
},
onRequest: () => {
setLoading(true);
},
onError: (ctx) => {
if (ctx.error.status === 403) {
toast.error(`You need a verified email address to login, an email has been sent to you.`);

} else {
toast.error(ctx.error.message);
}
},
onSuccess: () => {
toast.success("Login successful, redirecting...");
},
},
});
signIn.email({
email: values.email,
password: values.password,
callbackURL: "/dashboard",
fetchOptions: {
headers: {
"x-captcha-response": turnstileToken
},
onResponse: () => {
setLoading(false);
},
onRequest: () => {
setLoading(true);
},
onError: (ctx) => {
if (ctx.error.status === 403) {
toast.error(`You need a verified email address to login, an email has been sent to you.`);

} else {
toast.error(ctx.error.message);
}
},
onSuccess: () => {
toast.success("Login successful, redirecting...");
},
},
});
The issue is that both of the following failure cases return the same 403 status: - When the user hasn’t verified their email. - When the captcha check fails. The frontend can’t tell them apart, so users get the same error message either way — which is confusing and bad UX.
1 Reply
shadow
shadowOP•4d ago
Suggestion: It’d be much cleaner if the backend returned a more specific error code or identifier in the response body. For example:
{
"status": 403,
"code": "EMAIL_NOT_VERIFIED",
"message": "You need a verified email address to login."
}
{
"status": 403,
"code": "EMAIL_NOT_VERIFIED",
"message": "You need a verified email address to login."
}
And
{
"status": 403,
"code": "CAPTCHA_FAILED",
"message": "Captcha verification failed."
}
{
"status": 403,
"code": "CAPTCHA_FAILED",
"message": "Captcha verification failed."
}
This would allow the frontend to handle each case with proper context, like:
switch (ctx.error.code) {
case "EMAIL_NOT_VERIFIED":
toast.error("Please verify your email address. A verification link has been sent.");
break;
case "CAPTCHA_FAILED":
toast.error("Captcha failed. Please try again.");
break;
default:
toast.error("Access denied.");
}
switch (ctx.error.code) {
case "EMAIL_NOT_VERIFIED":
toast.error("Please verify your email address. A verification link has been sent.");
break;
case "CAPTCHA_FAILED":
toast.error("Captcha failed. Please try again.");
break;
default:
toast.error("Access denied.");
}
Let me know if this is already possible and I missed something — or if there’s a workaround. Thanks! 🙌

Did you find this page helpful?