← Writing

Firebase has no transaction spanning Auth and Firestore — order your writes for it

Creating a user doc and then failing Auth creation leaves half-baked data, because nothing rolls back across Firebase services. Put the failable external call first, or clean up on failure.

A flow for adding a team member to a tenant taught me this one: the code created the Firestore user document first, then created the Firebase Auth account — and Auth creation can fail, most commonly because the email already exists in another tenant. Firestore transactions don’t span Firebase Auth. Nothing rolls back. The result was half-baked data: a user doc for an account that doesn’t exist, waiting to confuse every query that finds it.

The fix is ordering, not cleverness. Put the failable external call first: create the Auth account, and only write the Firestore doc once Auth has succeeded. If your flow genuinely can’t be reordered, then every step after the first needs an explicit compensating cleanup on failure — delete what you created, and treat the cleanup itself as fallible code that deserves a log line when it doesn’t run.

Two smaller lessons rode along. First, surface the real error: the user saw a generic failure when the actual cause was “this email belongs to another account” — the one message that would have told them exactly what to do. Second, we removed a redundant client-side duplicate-email check. It sounds helpful, but the server is the only authority on uniqueness; the client check just raced it and masked the authoritative answer. Let the server answer, and translate that answer honestly for the user.

General form: any time one logical operation writes to two services with no shared transaction, decide up front which write is the anchor, do it first, and make everything downstream either idempotent or cleaned up.

← All writing Book a call →
Book a call → WhatsApp