{"record":{"id":"80643ab207e8c2e2","repo":"plandex-ai/plandex","slug":"invalid-or-expired-pin","errorCode":null,"errorMessage":"Invalid or expired pin","messagePattern":"Invalid or expired pin","errorType":"http","errorClass":"http","httpStatus":404,"severity":"warning","filePath":"app/server/handlers/sessions.go","lineNumber":162,"sourceCode":"\t\tlog.Printf(\"Error reading request body: %v\\n\", err)\n\t\thttp.Error(w, \"Error reading request body: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tvar req shared.VerifyEmailPinRequest\n\terr = json.Unmarshal(body, &req)\n\tif err != nil {\n\t\tlog.Printf(\"Error unmarshalling request: %v\\n\", err)\n\t\thttp.Error(w, \"Error unmarshalling request: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\treq.Email = strings.ToLower(req.Email)\n\n\t_, err = db.ValidateEmailVerification(req.Email, req.Pin)\n\n\tif err != nil {\n\t\tif err.Error() == db.InvalidOrExpiredPinError {\n\t\t\thttp.Error(w, \"Invalid or expired pin\", http.StatusNotFound)\n\t\t\treturn\n\t\t}\n\n\t\tlog.Printf(\"Error validating email verification: %v\\n\", err)\n\t\thttp.Error(w, \"Error validating email verification: \"+err.Error(), http.StatusInternalServerError)\n\t\treturn\n\t}\n\n\tlog.Println(\"Successfully verified email pin\")\n}\n\n// sign in codes allow users to authenticate between different clients\n// like UI to CLI or vice versa\nfunc CreateSignInCodeHandler(w http.ResponseWriter, r *http.Request) {\n\tlog.Println(\"Received request for CreateSignInCodeHandler\")\n\n\tauth := Authenticate(w, r, true)\n","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/plandex-ai/plandex/blob/e2d772072efadbe41d2946d97d79be55532dbab5/app/server/handlers/sessions.go#L144-L180","documentation":"A 404 'Invalid or expired pin' is returned by CheckEmailVerificationHandler when db.ValidateEmailVerification returns the sentinel db.InvalidOrExpiredPinError. This means the submitted PIN does not match any active verification record for the email — it was mistyped, already used, or its TTL elapsed. Unlike the neighboring 500, this is an expected client-facing outcome, not a server fault.","triggerScenarios":"POST to verify-email-pin with a pin that: was typed incorrectly, was already consumed by a prior successful verification, belongs to an expired verification window, or does not match the row created for req.Email (email typo/case mismatch — server lowercases the email before lookup).","commonSituations":"User waits past the PIN expiration and retries an old code; user requests a new PIN and submits the older one; email client renders the PIN with extra whitespace; duplicate verification requests invalidate earlier codes.","solutions":["Request a fresh verification PIN and use the newest code from the most recent email","Submit the PIN exactly as sent (6 alphanumeric chars, no whitespace)","Ensure the email matches the one the PIN was issued to (case-insensitive)","Check the email_verifications table for expiration timestamps if debugging server-side","Automate re-issue on the client when a 404 invalid-pin response arrives"],"exampleFix":"// before: resubmitting a possibly stale pin forever\nfor {\n\terr := verifyPin(email, pin)\n\tif err != nil { /* retry same pin */ }\n}\n// after: request a fresh pin on invalid/expired 404\nif resp.StatusCode == http.StatusNotFound {\n\tif err := requestNewVerification(email); err != nil { return err }\n\tpin = awaitNewPin(email)\n}","handlingStrategy":"fallback","validationCode":"// client side: sanity-check before submitting\nif len(pin) != 6 || strings.TrimSpace(pin) != pin {\n\treturn fmt.Errorf(\"pin must be exactly 6 characters with no whitespace\")\n}","typeGuard":null,"tryCatchPattern":"// Go client: treat 404 as expected, re-issue a pin\nresp, err := http.Post(url, \"application/json\", body)\nif err != nil { return err }\nif resp.StatusCode == http.StatusNotFound {\n\t// invalid or expired: request a fresh verification pin\n\treturn requestNewVerification(email)\n}","preventionTips":["Auto-request a new PIN when a 404 invalid-pin response arrives","Show a countdown of PIN expiry in the UI","Discard older PINs when a new one is issued","Strip whitespace from PIN input fields before submit"],"tags":["pin","expiration","http-404","email-verification"],"backgroundTag":"invalid-or-expired-pin","analyzedSha":"e2d772072efadbe41d2946d97d79be55532dbab5","analyzedAt":"2026-09-05T20:56:53.631Z","contentChangedAt":"2026-09-05T20:56:53.631Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}