Skip to Content

Members are told when their passcode email is rejected

Shipped 2026-09-11

TUN-859 made email send failures visible in Datadog. This makes them visible to the member, who is the person actually locked out.

sendEmailPasscode() called sendPasscodeToEmail() without awaiting it and then returned status: true with “passcode sent to (ca***@l***.net)”. A member whose address had just bounced was told their code was on its way to a specific mailbox, waited, retried, and was told exactly the same thing again. sendPasscodeToEmail() compounded it by logging passcode.sent whatever happened, so the record asserted a delivery that had not taken place either.

Four real cases turned up in the first two days of TUN-859’s reporting, across two members, every one a soft bounce.

The send is now awaited, and a rejection returns a verdict the login screen can act on: the reason, whether it was transient, and what this member can do instead. A member with a verified phone is offered the code by SMS. A member without one is told plainly that there is no second channel on their account.

sendPhonePasscode() has always checked its result and returned a failure — this brings the email path into line with its sibling rather than inventing a pattern.

Why security questions are not offered

They were built and removed. Security questions lead only to a password reset, and the member’s password is fine in this situation — it is their second factor that is broken. Following that route ends at the same failing passcode step, so it is a loop rather than a way in. A genuine last-resort access route is being worked on separately.

Found along the way

Ten logger.info and logger.warn calls in the passcode service used a message: key. Winston hoists that key out of the object it is given and leaves the remaining fields to be appended as a second line, so every field those calls were written to record was reaching Datadog detached from its label. They now use event:, which keeps the object intact. Twelve more calls elsewhere have the same fault, tracked as TUN-868.

Last updated on