VIN input shows misleading "I/O/Q not allowed" error for masked VINs (asterisks) — causes rage-clicks #89
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Users frequently paste a privacy-masked VIN — e.g.
VF7S1VJZ*********,TMAJB81A*********— copied from a marketplace listing, a screenshot, or a document that hides part of the chassis number. On/dashboard/search:*characters and the user is shown:Result: the user re-reads the error, retries the same masked VIN, and rage-clicks because the message does not match what they typed.
Evidence (file:line —
apps/web)apps/web/src/routes/dashboard/search.tsx:35-51—sanitizeVin()only auto-corrects[IOQioq]. Asterisks and other non-alphanumerics pass through unchanged; there is no inline validation of the remaining characters before submit.apps/web/src/routes/dashboard/search.tsx:552—maxLength={17}, and:598— submit is disabled only whenvin.length !== 17. A 17-char masked VIN keeps the button enabled.apps/web/src/routes/dashboard/search.tsx:18—VIN_REGEX = /^[A-HJ-NPR-Z0-9]{17}$/.apps/web/src/routes/dashboard/search.tsx:318-326— on submit,isValidVin()rejects*, firessearch_input_validation_failed { error_type: "invalid_chars" }, and setserror = t("search.errorInvalidVin").apps/web/src/messages/tr.json:677(anden.json:677) — the only invalid-VIN message mentions length + I/O/Q, never masked/invalid characters.apps/api/src/common/pipes/vin-validation.pipe.ts:13-16andapps/api/src/vehicles/vehicles.service.ts:92-94. (Frontend blocks first, so the backend isn't reached in this masked-VIN case — but it has the same UX gap if hit directly.)Data
In
posthog_events, all 19search_input_validation_failedevents areerror_type=invalid_charswithinput_length=17. BecausesanitizeVinalready strips I/O/Q on input, a 17-charinvalid_charsfailure can only be caused by characters outside[A-HJ-NPR-Z0-9IOQ]— i.e. asterisks/masks/punctuation — which is the masked-VIN signature. The VIN value is intentionally not captured on this event (search.tsx:319-323), so the literal masked strings live in session recordings.Reproduction
/dashboard/search.VF7S1VJZ*********(8 chars + 9 asterisks = 17).Proposed fix (frontend, small, localized to
search.tsx)handleVinChange/sanitizeVin: any char that is not VIN-legal and not an auto-correctable I/O/Q — specifically*, spaces, dashes, punctuation.search.errorMaskedVin(tr): "VIN numarası maskelenmiş görünüyor (yıldız/eksik karakter içeriyor). Lütfen tam 17 karakterlik şase numarasını girin."search.errorMaskedVin(en): "This VIN looks masked (it contains*or hidden characters). Please enter the full 17-character VIN."Branch to it in
handleSearchwhen the rejected input contains*or other non-alphanumerics, instead of always usingerrorInvalidVin.VinValidationPipemessage so an asterisk-containing VIN returns a mask-specific 400 reason too, for API parity.Acceptance criteria
*shows a mask-specific message (not the I/O/Q message).tr.jsonanden.json.VinValidationPipereturns a mask-specific reason for asterisk VINs.Verification checklist (confirm "fixed")
VF7S1VJZ*********and asserts the OLD misleading message appears (captured before merge as the failing baseline).NMTKH4BX60R107701decodes; (b)vinwith lowercase i/o/q auto-corrects; (c) 16-char input shows length error.search_input_validation_failedevents witherror_type=invalid_chars&input_length=17(the masked-VIN signature) trend down; ideally add a newerror_type=masked_vinto make this directly measurable./dashboard/searchfor sessions that hit the masked path (check session recordings /$rageclick).cmpe55g5in the panel does not recur (pipeline will auto-flag asregressedif it does).Notes / scope
9a479f9("distinguish client-side VIN validation rejects from provider failures") is in the observability/panel repo, not this product — it changes how insights are classified, not product VIN handling. No product commit addresses masked VINs.Filed from Süper Panel insight triage (2026-06-03). Covers insight(s): cmpe55g570. Canonical:
cmpe55g57000mflajghytzgb6.🤖 Fusion task opened:
FN-435Triage queue: https://fusion.semih.ai/tasks/FN-435
This comment was posted automatically by the
fusion-plugin-gitea-issuesbridge. A Fusion agent will update this issue when the task moves to in-progress, in-review, or done.