X_MFA_SECRET_INVALID
Means
a TOTP secret that is not RFC 4648 base32, so it decodes to zero bytes
Typical cause
enrolTotp is handed an imported secret outside the alphabet, or totpCode is called with one. verifyTotp does not throw it — an unreadable stored secret reads as a non-verdict there, the same rule verifyAgainst follows for a password hash Bun cannot read, so a login checking a broken row gets a credential refusal rather than a 500
Fix
issue a fresh one and store it: const { secret } = enrolTotp(auth, { account: user.email }). An imported secret must be base32 (A-Z and 2-7; padding, spaces and dashes are ignored) and must decode to at least one byte. Zero bytes is not a decode that failed closed: an HMAC keyed with zero bytes is a valid HMAC, so every unreadable secret in the table used to verify against one shared code stream that needed no secret to compute