{"record":{"id":"fda5cd85a61b0a58","repo":"usestrix/strix","slug":"invalid-code","errorCode":"invalid_code","errorMessage":"invalid_code","messagePattern":"invalid_code","errorType":"error_code","errorClass":"RelayError","httpStatus":403,"severity":"warning","filePath":"strix/interface/viewer/auth.py","lineNumber":196,"sourceCode":"    raise RelayError(\"unavailable\")\n\n\ndef otp_verify(email: str, code: str) -> dict[str, Any]:\n    \"\"\"Verify a code. Returns ``{token, email, expires_at}`` or raises RelayError.\"\"\"\n    status, data = _post_json(\n        \"/api/oss/otp/verify\",\n        {\"email\": email, \"code\": code},\n        timeout=_OTP_TIMEOUT,\n    )\n    if status == 200 and isinstance(data.get(\"token\"), str):\n        # A token with no usable expiry cannot unlock history locally (the gate\n        # fails closed), so treat such a response as a failed verification rather\n        # than reporting success and then leaving the user stuck unverified.\n        if parse_expiry(data.get(\"expires_at\")) is None:\n            raise RelayError(\"unavailable\")\n        return data\n    if status == 403:\n        raise RelayError(\"invalid_code\")\n    raise RelayError(\"unavailable\")\n\n\ndef feedback_submit(email: str, message: str) -> None:\n    \"\"\"Relay a feedback message + email to Strix. No verification is required;\n    the email is taken as given. Raises RelayError on failure.\"\"\"\n    status, data = _post_json(\n        \"/api/oss/feedback\",\n        {\"email\": email, \"message\": message},\n        timeout=_OTP_TIMEOUT,\n    )\n    if status == 200:\n        return\n    if status == 429:\n        raise RelayError(\"rate_limited\")\n    if status == 400:\n        code = data.get(\"error\")\n        if code in (\"invalid_email\", \"invalid_message\"):","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/usestrix/strix/blob/85513391305171ecc6faffe03da4a8bda5e3febb/strix/interface/viewer/auth.py#L178-L214","documentation":"RelayError('invalid_code') raised by otp_verify() when POST /api/oss/otp/verify returns 403 — the submitted OTP code does not match the one the relay emailed, or it has expired. This is the single 'wrong code' signal from the verification flow.","triggerScenarios":"Calling otp_verify(email, '000000') with a mistyped or transposed code; submitting after the code's validity window elapsed; re-submiting a code that was already consumed; case/whitespace corruption when the code is copied from the email or typed by hand.","commonSituations":"User typo entering the 6-digit code; code expired because the user waited too long before entering it; email client rendering the code with hidden characters; autofill inserting an old code from a previous verification.","solutions":["Re-enter the code carefully, copying it verbatim from the newest email (watch for similar glyphs like 0/O, 1/l)","If it may have expired, request a fresh code via otp_start() and use that one within its validity window","Strip whitespace and normalize case before submitting: code.strip().upper() if codes are case-insensitive per relay format","Guard against autofill/old codes: always use the most recent email, not a pinned one"],"exampleFix":"# before\notp_verify(email, input('code: '))     # pasted with trailing space -> invalid_code\n# after\notp_verify(email, input('code: ').strip())","handlingStrategy":"try-catch","validationCode":"import re\n\ndef plausible_code(code: str) -> bool:\n    c = code.strip()\n    return bool(re.fullmatch(r\"[0-9A-Za-z]{4,10}\", c))","typeGuard":null,"tryCatchPattern":"try:\n    otp_verify(email, code)\nexcept RelayError as e:\n    if e.code == \"invalid_code\":\n        code = prompt(\"wrong or expired code; re-enter or request a new one\").strip()\n        otp_verify(email, code)\n    else:\n        raise","preventionTips":["Strip whitespace from hand-typed or pasted codes","Offer a 'request new code' action after 2-3 failed attempts","Use the newest email; disable autofill of old codes"],"tags":["strix","relay","viewer","auth","otp"],"backgroundTag":null,"analyzedSha":"85513391305171ecc6faffe03da4a8bda5e3febb","analyzedAt":"2026-08-15T05:03:57.275Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}