Skip to content

fix(login): recover Turnstile widget from transient errors - #3306

Draft
posthog-eu[bot] wants to merge 1 commit into
mainfrom
posthog-self-driving/fixlogin-stop-recurring-turnstile-eb2b6c
Draft

fix(login): recover Turnstile widget from transient errors#3306
posthog-eu[bot] wants to merge 1 commit into
mainfrom
posthog-self-driving/fixlogin-stop-recurring-turnstile-eb2b6c

Conversation

@posthog-eu

@posthog-eu posthog-eu Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Summary

Recurring login failures traced to Turnstile error 300010 on the captcha-protected login page. Users get stranded on a dead captcha and cannot sign in.

  • Why: src/pages/login.vue marked the Turnstile widget permanently unavailable on the first @error and never reset it. Error 300010 and the rest of the 3xxxxx/6xxxxx families are transient (network, WebView, ad-blocker) and clear after a widget reset — but the page had no recovery path, and each hit logged an error-level line.

Changes:

Area Before After
Recoverable error (300010 etc.) Widget dead on first error, error logged each time Reset and retry a bounded number of times; log at error level only after the budget is spent
Token expiry @expired ignored; stale token stayed bound (Cloudflare does not reset on expiry) Clear the token and reset for a fresh challenge
Blocked user No feedback — silent failure Show the existing captcha-not-available message

Non-recoverable codes (bad sitekey 110xxx, unsupported browser) still fail fast — retrying would not help.

Retry and recoverability logic lives in src/utils/turnstile.ts as pure functions, so it is unit-tested directly.

Note

The sso_error=enforcement_check_failed redirect seen in the same login URLs comes from a separate router guard (src/modules/sso-enforcement.ts) that fails closed on an unreachable enforcement check. That mechanism works as designed and is out of scope here; this PR targets the high-volume Turnstile captcha lifecycle.

Test plan

  • bun test:unit — new cases in tests/turnstile.unit.test.ts cover error recoverability (300010/600010 recoverable; 110200/100000 not) and the retry budget (retry while under budget, give up once spent).
  • Manual: on the login page, force a Turnstile @error with a 3xxxxx code and confirm the widget resets and a token is obtained instead of the form locking up; force a non-recoverable code and confirm the "Captcha not available" message appears.
Agent context
  • The vue-turnstile error-callback only re-emits the code and returns undefined, so the widget does not self-recover — an explicit reset() is required. Its expired-callback likewise does not reset the widget.
  • Live reproduction of 300010 needs specific network/browser conditions (Cloudflare-side), so verification is via the pure-function unit tests plus the affected session recording referenced in the report.
  • The shared scratchpad MCP tools were not available in this run, so no fleet memory was read or written.

Screenshots

Not included — the change adds a captcha-error recovery path and a text message; no new visual layout.

Checklist

  • My code follows the code style of this project and passes
    bun run lint:backend && bun run lint. (Followed the project style;
    lint not run in this environment — dependencies could not be installed.)
  • My change requires a change to the documentation.
  • I have updated the documentation
    accordingly.
  • My change has adequate E2E test coverage. (Added unit tests in
    tests/turnstile.unit.test.ts; no E2E added.)
  • I have tested my code manually. Live 300010 needs Cloudflare-side
    conditions; verified via unit tests and the repro steps above.

Created with PostHog Desktop from this inbox report.


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

Review in cubic

The login captcha marked the widget permanently unavailable on the first
Turnstile error and never reset it. Error 300010 and the rest of the
3xxxxx/6xxxxx families are transient (network, WebView, ad-blocker), so a
single hiccup stranded the user on a dead captcha and logged an error each
time.

- Reset and retry the widget a bounded number of times on a recoverable
  error; give up (and log at error level) only after the budget is spent.
- Handle the expired-token callback: Cloudflare does not reset the widget on
  expiry, so clear the stale token and reset for a fresh challenge.
- Show the existing "Captcha not available" message when the widget is
  unavailable so a blocked user sees why.
- Add unit tests for error recoverability and the retry budget.

Generated-By: PostHog Desktop
Task-Id: 59e9c12b-6ee3-4205-80fe-17c5ff529793
@posthog-eu
posthog-eu Bot deployed to deepsec-pr September 11, 2026 06:42 Active
@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 4b0e044b-4467-4372-9ca5-b8052a0de1d0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codspeed-hq

codspeed-hq Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 43 untouched benchmarks
⏩ 2 skipped benchmarks1


Comparing posthog-self-driving/fixlogin-stop-recurring-turnstile-eb2b6c (43385ef) with main (326249b)

Open in CodSpeed

Footnotes

  1. 2 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports.

@sonarqubecloud

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants