fix(errors): name three refusals that fell back to a generic sentence

Since a codeless refusal reads by its status, three answers lost their
meaning. A failed ChatGPT sign-in poll said "Error saving" instead of
asking to start the sign-in again; it now falls back to the sign-in's
own failure sentence. A dose-history import still running when the
card stopped watching said "Nothing was written" although the job may
still write; that case now has its own sentence. Setting up an
authenticator while one is active answered "setup failed"; the route
now sends `auth.mfa.already_active`, which says to disable the active
one first, in every locale.
This commit is contained in:
Marc-André Bombeck committed 2026-11-12 12:58:52 +01:00
1 parent 31c4440154
commit 8c9fbbdd2b
17 files changed
+116 -11

No files matched your search

+7 -1
View File
@@ -37,6 +37,8 @@ import { checkRateLimit, rateLimitHeaders } from "@/lib/rate-limit";
import { generateTotpSecret, buildOtpauthUri } from "@/lib/auth/mfa/totp";
import { checkCookieEnrollmentProof } from "@/lib/auth/existing-factor-proof";
const TOTP_ALREADY_ACTIVE_CODE = "auth.mfa.already_active";
export const dynamic = "force-dynamic";
const SETUP_RATE_LIMIT = 5;
@@ -64,7 +66,11 @@ export const POST = apiHandler(async (req: Request) => {
// live secret out from under the user's authenticator.
if (user.totpConfirmedAt) {
annotate({ action: { name: "auth.mfa.totp.setup.already_active" } });
throw new HttpError(409, "A second factor is already active");
throw new HttpError(
409,
"A second factor is already active",
TOTP_ALREADY_ACTIVE_CODE,
);
}
if (auth.transport === "cookie") {