

The problem
Try on shopper-side widget session completion sat at 48%.
Shoppers opened the widget, accepted consent, uploaded a photo and left before generation even started. Nothing had gone slowly, nothing had produced a bad result. There was no effort to abandon yet.
We couldn't look at why. We don't store shopper images or personal data, so every diagnosis here had to be inferred from system events alone.

The finding
I instrumented that section of the funnel and the drop resolved to one place: the gate.
The gate is our input analyzer. It blocks unusable photos before generation, because a bad input produces a bad try-on, costing the shopper their result and the merchant a token.
It was doing its job. It just wasn't telling anyone.

Shoppers kept trying, and kept failing
Sessions showed 12 gate blocks against the same user ID before the widget closed. Meaning repeating the same failure told them nothing.
We were counting the gate as our failure
Our own system failure rate was inflated, because we'd been counting gate rejections as our failures.
The fix
1
Reflect the real reasons
The fastest test of the hypothesis was to pass the backend's actual rejection reason to the frontend. Completion moved to 50% , but if unclear messaging had been the whole problem, this should have reached close to ceiling.
Fix what didn't need explaining
I analyzed the rejections themselves. The most frequent ones were what we could solve. (no-person/ multiple-people detected in the image or oversized files).
By sorting the rejections based on what we could solve vs. what genuinely required a better photo, and concentrating on solving our part, the completion moved again to 54%.
2
Stop calling it an error
The data showed shoppers were ignoring the message rather than misreading it: landscape rejections produced repeated attempts with identical results. The problem was the UI, not the copy.
A dismissible error with no direction reads as something that happened to the system, not something asked of you.
3

So the final iteration presented rejection as a gate rather than a failure. Non-dismissible, bottom sheet on mobile and modal on desktop, built around the specific reason: what blocked it, what to do, why it matters. Retry or exit, nothing else.
Then, prevention.
Three requirements cover most rejections — vertical photo, one person, clean background — so I moved them into permanent view instead of below the fold where the previous iteration had buried them. A second tips surface appears only after a photo passes, so first-timers aren't given everything at once.
4
The Catch
The gate is the one thing in this system that marks its own work. Blocked photos aren't stored, so a wrong rejection leaves nothing behind to find. If it were over-blocking, the only symptom would be completion falling, and it rose.
It's also why we'll never reach 100%. Some photos genuinely can't produce a good try-on, and a mediocre result isn't worth having: it loses the shopper and burns the merchant's token.

The result
Session completion 48% → 59%. Reported system failure rate dropped 13.7 points once gate rejections stopped counting as breakage.
Four iterations, and the one that moved it most added a blocking step to the flow.







