Skip to content

Commit fa94448

Browse files
committed
fix(clerk-js): Enhance captcha failure logging with timing and container diagnostics
- Use crypto.randomUUID() for attempt IDs when available - Track error timeline with relative timestamps (ms from start) - Add lastErrorCode for easier filtering/grep - Check containerExistsAtFailure to diagnose 200100 race conditions - Include totalDurationMs for overall timing context
1 parent 8f1160d commit fa94448

1 file changed

Lines changed: 19 additions & 5 deletions

File tree

packages/clerk-js/src/utils/captcha/turnstile.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,15 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
7676
const { siteKey, widgetType, invisibleSiteKey, nonce } = opts;
7777
const { modalContainerQuerySelector, modalWrapperQuerySelector, closeModal, openModal } = opts;
7878
const captcha: Turnstile.Turnstile = await loadCaptcha(nonce);
79-
const errorCodes: (string | number)[] = [];
79+
80+
// Timing and error tracking for diagnostics
81+
const startTime = Date.now();
82+
const errorTimeline: Array<{ code: string | number; t: number }> = [];
8083
// Unique ID to correlate log entries for this captcha attempt
81-
const captchaAttemptId = Math.random().toString(36).substring(2, 9);
84+
const captchaAttemptId =
85+
typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function'
86+
? crypto.randomUUID()
87+
: Math.random().toString(36).substring(2, 9);
8288

8389
let captchaToken = '';
8490
let id = '';
@@ -184,7 +190,7 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
184190
}
185191
},
186192
'error-callback': function (errorCode) {
187-
errorCodes.push(errorCode);
193+
errorTimeline.push({ code: errorCode, t: Date.now() - startTime });
188194
/**
189195
* By setting retry to 'never' the responsibility for implementing retrying is ours
190196
* https://developers.cloudflare.com/turnstile/reference/client-side-errors/#retrying
@@ -196,7 +202,7 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
196202
}, 250);
197203
return;
198204
}
199-
reject([errorCodes.join(','), id]);
205+
reject([errorTimeline.map(e => e.code).join(','), id]);
200206
},
201207
'unsupported-callback': function () {
202208
reject(['This browser is not supported by the CAPTCHA.', id]);
@@ -224,13 +230,21 @@ export const getTurnstileToken = async (opts: CaptchaOptions) => {
224230
captcha.remove(id);
225231
}
226232

233+
// Check if widget container exists at failure time (helps diagnose 200100 race conditions)
234+
const containerExistsAtFailure = widgetContainerQuerySelector
235+
? !!document.querySelector(widgetContainerQuerySelector)
236+
: false;
237+
227238
// Log failure with full error history for debugging
228239
debugLogger.error('Turnstile captcha challenge failed', {
229240
captchaAttemptId,
230-
errorCodesHistory: [...errorCodes],
241+
errorTimeline,
242+
lastErrorCode: errorTimeline.length > 0 ? errorTimeline[errorTimeline.length - 1].code : null,
231243
finalError: String(e),
232244
retriesAttempted: retries,
233245
widgetType: captchaTypeUsed,
246+
containerExistsAtFailure,
247+
totalDurationMs: Date.now() - startTime,
234248
}, 'captcha');
235249

236250
// eslint-disable-next-line @typescript-eslint/only-throw-error

0 commit comments

Comments
 (0)