Skip to content

[RUM] Sign-up conflict is still a dead end after #1613 — 16 of 16 sessions in 7 days retried ~3× and none created an account #1668

Description

@dawsontoth

Summary

The client-side mitigation for #1612 shipped in #1613 and is live in production, and the
outcome it was meant to change has not moved at all
. In the last 7 days, 16 of 16
sessions
that hit a 409 on POST /User/ retried an average of 2.8 times and not one
of them created an account
— statistically identical to the 2.5 retries / 7-of-8
abandonment that motivated the original issue.

The reason is known and recorded in #1613: the 409 handling was deliberately left
status-agnostic because HarperFast/central-manager#630 was going to remove the 409
shortly. That was 19 days ago and #630 is still open, so Studio is running the
narrowed client path against a server that still emits the conflict.

Surfaced by the daily automated RUM review (RUM app f590deee-…, env:prod, windows as noted).

The numbers

POST /User/, production:

Window 409 events 409 sessions 201 events 201 sessions
24h → now 13 5 10 10
48h → 24h 0 0 8 8
72h → 48h 6 2 11 11
7d 45 16 115 115
30d 79 29 193 187

Conversion, measured by session-id set intersection over the 7-day window: 0 of 16.
No session that received a 409 subsequently received a 201 — not on a retry, not later
in the same session.

Before/after the #1613 fix, on the metric the fix targeted:

Retries per affected session Sessions that never created an account
Pre-fix (#1612, 24h to 2026-08-12) 20 / 8 = 2.5 7 of 8
Post-fix (7d to 2026-08-31) 45 / 16 = 2.8 16 of 16

Volume is also up, not down: #1612 measured "~19 409s in the preceding 30 days total".
It is now 79 in 30 days, and 76 of those 79 fall in the last 14 days.

The per-session signature is unchanged and remarkably uniform — five sessions in the last
24h, each exactly three POST /User/ requests roughly 4.5 s apart, then nothing:

02:30:40  02:30:44  02:30:50
08:39:57  08:40:01  08:40:06
11:30:54  11:30:58  11:31:03
11:55:58  11:56:04  11:56:09
01:28:55  01:28:59  01:29:04

Server latency on these is 0.3–1.8 s, so the gaps are a person waiting for the button to
re-enable and pressing it again.

This is not a deploy gap

Verified rather than assumed:

  • RUM reports exactly one version across all 54 sessions in the last 24h: v2.169.3.
  • 2bd2f64a, 3d1795ad, and 4a7b85fd (the sign-up form commits) are all ancestors of
    v2.169.3.
  • The chunk hash in today's error stacks (assets/index-D9wn3O9N.js) matches what
    https://fabric.harper.fast/ serves right now.

So the isPending guard and the persistent inline message are definitely running.

Mechanism

The server still answers a duplicate address with a hard conflict —
central-manager/src/resources/user/addUser.js:20:

throw new ClientError('User already exists', ERROR_CODE.CONFLICT);

Studio surfaces that text verbatim. SignUp's onError is deliberately status-agnostic
and sets root from describeError(error).message, which for this response resolves to
the RFC 9457 title — i.e. the string the user reads above the submit button is exactly
"User already exists". That path is covered by
src/features/auth/SignUp.test.tsx (surfaces a %i rejection inline, the 409 case), so
this is confirmed behavior, not inference.

So the person is told, persistently and in the right place, that the account exists — and
still resubmits the same details three times and leaves. The message states a fact and
names no next action; the "Already have an account? Sign in instead." link is below the
form, outside the block their attention is in. #1613 could not add that instruction
because the wording comes from the server and the form maps no status codes by design.

What to do

Ordered by how much each actually changes the outcome:

  1. Ship HarperFast/central-manager#630. It replaces the 409 with a uniform
    "check your email to continue" response, which ends the enumeration oracle and the
    dead end in one edit — the address either gets an email or gets a "you already have an
    account" email, and nobody is left staring at a form. Note its follow-on constraint,
    HarperFast/central-manager#636: the uniform response must carry a body, or
    onSignUpSubmit's if (data) … else throw turns a successful signup into
    "Something went wrong".
  2. Interim, status-agnostic, no oracle: render the existing "Already have an account?
    Sign in instead." link adjacent to the inline error whenever a submit error is
    present. The link is already unconditionally on the page, so promoting it on any
    failure leaks nothing new and maps no status codes — it stays within fix(auth): report a failed sign-up in the form, not a fading toast #1613's rule.
    Worth doing only if fix(deps): update dependency lucide-react to ^0.542.0 #630 is not imminent; it is a mitigation, not the fix.

I have deliberately not opened a PR re-introducing 409-specific copy. That was
explicitly rejected on review of #1613 (encoding "409 means the address is taken" in the
UI would turn closing the enumeration oracle into "you're removing a helpful message"),
and nothing in this data changes that argument.

Why this is worth reopening the thread rather than waiting

Sign-up is the top of the funnel and this is the only failure mode on it. A third of
sign-up sessions in the last 24h (5 of 15 that submitted) hit it, and the measured
recovery rate across 7 days is zero.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    blockedBlocked by a cross-project or external dependencyrumFrom real user monitoring where we aim to keep users happy

    Type

    Fields

    Priority

    P1

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions