Skip to content

fix(clerk-js): fail fast when the origin is slow at load#9065

Open
nikosdouvlis wants to merge 9 commits into
mainfrom
nikos/plat-2754-clerkjs-resiliency
Open

fix(clerk-js): fail fast when the origin is slow at load#9065
nikosdouvlis wants to merge 9 commits into
mainfrom
nikos/plat-2754-clerkjs-resiliency

Conversation

@nikosdouvlis

@nikosdouvlis nikosdouvlis commented Jul 1, 2026

Copy link
Copy Markdown
Member

Why
When Clerk's Frontend API is slow or unreachable, a cold Clerk.load() hangs for minutes. The /client request has no timeout, and on a 5xx or network error the recovery path awaits getToken({ skipCache: true }), which burns the full ~162s retry budget before Clerk is marked loaded. So during an origin outage the app sits there unresponsive instead of degrading and rendering.

What changed
Bound the browser load with a timeout in two spots: around the /client fetch and around the recovery token mint. On a slow or failed /client, recovery renders identity from the __session cookie, clears the session's token cache (via the existing clearCache()) so the mint bypasses the cache (no skipCache, so no force_origin), and keeps the cookie identity if the mint times out. Also adds a timeLimit util to @clerk/shared/utils.

One known gap: timeLimit stops waiting but doesn't cancel the request, so a timed-out recovery mint keeps retrying in the background and can briefly write a slightly stale token before the poller reconciles. Low severity and self-correcting, and the abortable-timeout follow-up plus the in-flight monotonic-session-token guards close it. This is phase 1, later phases cover fail-fast /environment and render-first init.

Summary by CodeRabbit

  • New Features
    • Added timeout-bounded, fail-fast client startup with improved background recovery so initialization can complete even when requests hang.
    • Improved cold-load identity availability using session-token-based fallback when available.
  • Bug Fixes
    • Abort in-flight requests on initialization timeouts; prevent overlapping recovery and polling.
    • Discard stale late client responses and improve degraded-state recovery, including correct stub-user behavior until full user data loads.
    • Stop retries when requests are aborted; added abort/cancellation support for client fetch and resource GET reads.
  • Tests
    • Expanded coverage for degraded startup, background client recovery, and timeout/abort scenarios.

When FAPI is slow or down, a cold clerk-js load hung for minutes: the /client fetch had no request timeout, and the load-failure recovery awaited getToken({ skipCache: true }), which runs a ~162s retry budget before Clerk marks itself loaded. During an outage the app sat unresponsive.

Bound the standard-browser load with a 5s timeout in two places: around the /client fetch, and around the recovery mint. On a slow or failed /client, recovery renders identity from the __session cookie, clears the session's token cache via the existing clearCache() so the mint bypasses the cache (no skipCache, so no force_origin), and awaits a fresh mint under the timeout, keeping the cookie identity if it times out. The on-timeout clear stops the poller from awaiting an abandoned in-flight mint.

Adds a timeLimit racer to @clerk/shared/utils.
@changeset-bot

changeset-bot Bot commented Jul 1, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 51cf079

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
Name Type
@clerk/clerk-js Patch
@clerk/shared Patch
@clerk/chrome-extension Patch
@clerk/electron Patch
@clerk/expo Patch
@clerk/astro Patch
@clerk/backend Patch
@clerk/expo-passkeys Patch
@clerk/express Patch
@clerk/fastify Patch
@clerk/headless Patch
@clerk/hono Patch
@clerk/localizations Patch
@clerk/msw Patch
@clerk/nextjs Patch
@clerk/nuxt Patch
@clerk/react-router Patch
@clerk/react Patch
@clerk/tanstack-react-start Patch
@clerk/testing Patch
@clerk/ui Patch
@clerk/vue Patch
@clerk/swingset Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
clerk-js-sandbox Ready Ready Preview, Comment Jul 8, 2026 10:26am
swingset Ready Ready Preview, Comment Jul 8, 2026 10:26am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a timeout-bounded Clerk load path, abort-aware client fetch propagation, JWT stub-user updates, and shared utility tests plus release notes.

Changes

Fail-fast initialization

Layer / File(s) Summary
timeLimit utility and export
packages/shared/src/utils/timeLimit.ts, packages/shared/src/utils/index.ts, packages/shared/src/utils/__tests__/timeLimit.test.ts, .changeset/clerkjs-fail-fast-slow-origin.md
timeLimit races a value or promise against a timeout, aborts an optional controller on timeout, is re-exported from the utils barrel, and is covered by tests for resolution, timeout rejection, and timer cleanup; the changeset documents the release.
Clerk.load() fail-fast and retry wiring
packages/clerk-js/src/core/clerk.ts, packages/clerk-js/src/core/fapiClient.ts, packages/clerk-js/src/core/resources/Base.ts, packages/clerk-js/src/core/resources/Client.ts, packages/clerk-js/src/core/jwt-client.ts, packages/clerk-js/src/core/__tests__/jwt-client.test.ts
Clerk.load() now times out the initial /client fetch and degraded recovery token minting, aborts timed-out requests, clears session cache during recovery, tracks client generations to drop stale background responses, propagates signal through resource reads, and sets the stub JWT client user timestamp to 1.
Load and JWT client tests
packages/clerk-js/src/core/__tests__/clerk.test.ts
Adds .load() tests for hanging fetch, fallback minting, degraded background retry, stale-response rejection, and 4xx rethrow handling, plus createClientFromJwt tests for JWT-derived session data and the empty-client case.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: bratsos, octoper

Poem

A rabbit raced the ticking gate,
No hanging load would sit and wait.
With timeLimit pinging: hop, hop, hop,
The stale old fetch would now just stop.
Fresh client crumbs and tokens bright,
Keep Clerk.js swift in dawn’s first light. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: making Clerk fail fast during slow origin load.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

@github-actions

github-actions Bot commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-07-08T10:27:09.595Z

Summary

Metric Count
Packages analyzed 19
Packages with changes 1
🔴 Breaking changes 0
🟡 Non-breaking changes 0
🟢 Additions 1

@clerk/shared

Current version: 4.25.0
Recommended bump: MINOR → 4.26.0

Subpath ./utils

🟢 Additions (1)

Added: timeLimit
+ declare function timeLimit<T>(value: T | PromiseLike<T>, ms: number, abortController?: Pick<AbortController, 'abort'>): Promise<T>;

Added function timeLimit


Report generated by Break Check

Last ran on 51cf079.

@pkg-pr-new

pkg-pr-new Bot commented Jul 1, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9065

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9065

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9065

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9065

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9065

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9065

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9065

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9065

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9065

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9065

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9065

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9065

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9065

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9065

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9065

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9065

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9065

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9065

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9065

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9065

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9065

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9065

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9065

commit: 51cf079

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/clerk-js/src/core/clerk.ts (2)

3184-3186: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Don't swallow auth failures from the recovery mint.

The outer /client path correctly rethrows 4xx at Lines 3184-3186, but the recovery session.getToken() branch converts every failure into null. If the cookie-backed session has already been revoked or expired, this preserves the cookie-derived identity and finishes load as signed-in degraded instead of clearing auth state. Only timeout/network failures should be tolerated here; explicit auth failures need to sign the user out or bubble up.

Also applies to: 3202-3214

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/clerk-js/src/core/clerk.ts` around lines 3184 - 3186, The recovery
mint flow in clerk.ts is swallowing explicit auth failures by turning every
session.getToken() error into null, which leaves revoked/expired cookie sessions
looking signed-in. Update the recovery branch around session.getToken() so only
timeout/network failures are tolerated and mapped to null; for auth-related
failures, rethrow or sign the user out and clear the recovered identity. Use the
existing is4xxError(e) handling in the /client path as the pattern, and apply
the same distinction in the session.getToken() branch referenced by the affected
recovery logic.

3162-3175: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Wrap Environment.getInstance().fetch() with the initialization timeout
fetchMaxTries only limits retries here; it does not cap wall-clock time. Promise.allSettled([initEnvironmentPromise, initClient()]) can still stall on a slow /environment request, so this branch should use the same timeLimit(INITIALIZATION_TIMEOUT_MS) guard as the client fetch.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/clerk-js/src/core/clerk.ts` around lines 3162 - 3175, The
initialization flow in clerk.ts should apply the same wall-clock timeout to the
Environment.getInstance().fetch() path as initClient(), since fetchMaxTries only
limits retries and Promise.allSettled can still hang on a slow /environment
request. Update the initEnvironmentPromise chain to wrap the fetch call with
timeLimit(INITIALIZATION_TIMEOUT_MS) before the then/catch handling, keeping the
existing updateEnvironment and fallback-to-SafeLocalStorage logic intact.
🧹 Nitpick comments (1)
packages/clerk-js/src/core/__tests__/clerk.test.ts (1)

830-834: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid any in the session double.

Line 830’s session: any removes the compiler checks around id, getToken(), and clearCache(), so this suite can drift away from the recovery-path contract without noticing. A tiny test-local interface keeps the helper honest.

As per coding guidelines, "Avoid any type - prefer unknown when type is uncertain, then narrow with type guards."

Suggested typing cleanup
+interface RecoverySessionDouble {
+  id: string;
+  status: 'active';
+  user: Record<string, unknown>;
+  getToken(): Promise<string | null>;
+  clearCache(): void;
+}
+
-const sessionClient = (session: any) => ({
+const sessionClient = (session: RecoverySessionDouble) => ({
   signedInSessions: [session],
   lastActiveSessionId: session.id,
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/clerk-js/src/core/__tests__/clerk.test.ts` around lines 830 - 834,
The session test double uses an untyped `any`, which bypasses compile-time
checks for the recovery-path contract. Update the `sessionClient` helper in
`clerk.test.ts` to use a small test-local interface or a narrowed `unknown` type
that explicitly includes the `id`, `getToken()`, and `clearCache()` members, and
keep `sessionlessClient` unchanged.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/clerk-js/src/core/__tests__/clerk.test.ts`:
- Around line 838-848: The helper `pumpUntilSettled` in `clerk.test.ts` can
still hang because it always awaits `tracked` even after the timer budget is
exhausted. Update `pumpUntilSettled` so that if the `load()` promise has not
settled after the 20 `vi.advanceTimersByTimeAsync(5000)` iterations, it throws a
deterministic error instead of awaiting forever; keep the existing settled
tracking logic and only await `tracked` when the promise has already resolved or
rejected.

---

Outside diff comments:
In `@packages/clerk-js/src/core/clerk.ts`:
- Around line 3184-3186: The recovery mint flow in clerk.ts is swallowing
explicit auth failures by turning every session.getToken() error into null,
which leaves revoked/expired cookie sessions looking signed-in. Update the
recovery branch around session.getToken() so only timeout/network failures are
tolerated and mapped to null; for auth-related failures, rethrow or sign the
user out and clear the recovered identity. Use the existing is4xxError(e)
handling in the /client path as the pattern, and apply the same distinction in
the session.getToken() branch referenced by the affected recovery logic.
- Around line 3162-3175: The initialization flow in clerk.ts should apply the
same wall-clock timeout to the Environment.getInstance().fetch() path as
initClient(), since fetchMaxTries only limits retries and Promise.allSettled can
still hang on a slow /environment request. Update the initEnvironmentPromise
chain to wrap the fetch call with timeLimit(INITIALIZATION_TIMEOUT_MS) before
the then/catch handling, keeping the existing updateEnvironment and
fallback-to-SafeLocalStorage logic intact.

---

Nitpick comments:
In `@packages/clerk-js/src/core/__tests__/clerk.test.ts`:
- Around line 830-834: The session test double uses an untyped `any`, which
bypasses compile-time checks for the recovery-path contract. Update the
`sessionClient` helper in `clerk.test.ts` to use a small test-local interface or
a narrowed `unknown` type that explicitly includes the `id`, `getToken()`, and
`clearCache()` members, and keep `sessionlessClient` unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: adbc0a12-845a-4bcc-8b5e-c59355326470

📥 Commits

Reviewing files that changed from the base of the PR and between c01b937 and d5ea434.

📒 Files selected for processing (6)
  • .changeset/clerkjs-fail-fast-slow-origin.md
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/shared/src/utils/__tests__/timeLimit.test.ts
  • packages/shared/src/utils/index.ts
  • packages/shared/src/utils/timeLimit.ts

Comment thread packages/clerk-js/src/core/__tests__/clerk.test.ts
@nikosdouvlis

Copy link
Copy Markdown
Member Author

!snapshot

@github-actions

This comment has been minimized.

Bound the load /client fetch and abort it on timeout instead of leaving it in flight. On a slow or failed /client, mint a fresh token first (clearCache + plain getToken, so no force_origin and the minter can still serve it during an origin outage) and derive the degraded identity from that token, falling back to the __session cookie identity only when the mint fails or times out. The mint is bounded by the same timeout.

After the degraded load, retry /client once in the background with no timeout and apply it only if nothing else updated the client meanwhile, so a sign-out or a piggybacked client wins over a late response.

Stamp the cookie-derived stub user with updated_at: 1 so listener memoization replaces it once the full user arrives, fixing useUser() returning the stub after recovery. Add a timeLimit util to @clerk/shared/utils that optionally aborts an AbortController on timeout.
@nikosdouvlis

Copy link
Copy Markdown
Member Author

!snapshot

@nikosdouvlis

Copy link
Copy Markdown
Member Author

!snapshot

@github-actions

github-actions Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Hey @nikosdouvlis - the snapshot version command generated the following package versions:

Package Version
@clerk/astro 3.4.12-snapshot.v20260703092711
@clerk/backend 3.11.0-snapshot.v20260703092711
@clerk/chrome-extension 3.1.48-snapshot.v20260703092711
@clerk/clerk-js 6.24.0-snapshot.v20260703092711
@clerk/electron 0.0.9-snapshot.v20260703092711
@clerk/electron-passkeys 0.0.4-snapshot.v20260703092711
@clerk/eslint-plugin 0.2.1-snapshot.v20260703092711
@clerk/expo 3.7.0-snapshot.v20260703092711
@clerk/expo-passkeys 1.2.0-snapshot.v20260703092711
@clerk/express 2.1.36-snapshot.v20260703092711
@clerk/fastify 3.1.46-snapshot.v20260703092711
@clerk/headless 0.0.7-snapshot.v20260703092711
@clerk/hono 0.1.46-snapshot.v20260703092711
@clerk/localizations 4.12.1-snapshot.v20260703092711
@clerk/msw 0.0.43-snapshot.v20260703092711
@clerk/nextjs 7.5.13-snapshot.v20260703092711
@clerk/nuxt 2.6.12-snapshot.v20260703092711
@clerk/react 6.11.4-snapshot.v20260703092711
@clerk/react-router 3.5.5-snapshot.v20260703092711
@clerk/shared 4.24.0-snapshot.v20260703092711
@clerk/swingset 0.0.14-snapshot.v20260703092711
@clerk/tanstack-react-start 1.4.13-snapshot.v20260703092711
@clerk/testing 2.2.3-snapshot.v20260703092711
@clerk/ui 1.24.2-snapshot.v20260703092711
@clerk/upgrade 2.0.5-snapshot.v20260703092711
@clerk/vue 2.4.11-snapshot.v20260703092711

Tip: Use the snippet copy button below to quickly install the required packages.
@clerk/astro

npm i @clerk/astro@3.4.12-snapshot.v20260703092711 --save-exact

@clerk/backend

npm i @clerk/backend@3.11.0-snapshot.v20260703092711 --save-exact

@clerk/chrome-extension

npm i @clerk/chrome-extension@3.1.48-snapshot.v20260703092711 --save-exact

@clerk/clerk-js

npm i @clerk/clerk-js@6.24.0-snapshot.v20260703092711 --save-exact

@clerk/electron

npm i @clerk/electron@0.0.9-snapshot.v20260703092711 --save-exact

@clerk/electron-passkeys

npm i @clerk/electron-passkeys@0.0.4-snapshot.v20260703092711 --save-exact

@clerk/eslint-plugin

npm i @clerk/eslint-plugin@0.2.1-snapshot.v20260703092711 --save-exact

@clerk/expo

npm i @clerk/expo@3.7.0-snapshot.v20260703092711 --save-exact

@clerk/expo-passkeys

npm i @clerk/expo-passkeys@1.2.0-snapshot.v20260703092711 --save-exact

@clerk/express

npm i @clerk/express@2.1.36-snapshot.v20260703092711 --save-exact

@clerk/fastify

npm i @clerk/fastify@3.1.46-snapshot.v20260703092711 --save-exact

@clerk/headless

npm i @clerk/headless@0.0.7-snapshot.v20260703092711 --save-exact

@clerk/hono

npm i @clerk/hono@0.1.46-snapshot.v20260703092711 --save-exact

@clerk/localizations

npm i @clerk/localizations@4.12.1-snapshot.v20260703092711 --save-exact

@clerk/msw

npm i @clerk/msw@0.0.43-snapshot.v20260703092711 --save-exact

@clerk/nextjs

npm i @clerk/nextjs@7.5.13-snapshot.v20260703092711 --save-exact

@clerk/nuxt

npm i @clerk/nuxt@2.6.12-snapshot.v20260703092711 --save-exact

@clerk/react

npm i @clerk/react@6.11.4-snapshot.v20260703092711 --save-exact

@clerk/react-router

npm i @clerk/react-router@3.5.5-snapshot.v20260703092711 --save-exact

@clerk/shared

npm i @clerk/shared@4.24.0-snapshot.v20260703092711 --save-exact

@clerk/swingset

npm i @clerk/swingset@0.0.14-snapshot.v20260703092711 --save-exact

@clerk/tanstack-react-start

npm i @clerk/tanstack-react-start@1.4.13-snapshot.v20260703092711 --save-exact

@clerk/testing

npm i @clerk/testing@2.2.3-snapshot.v20260703092711 --save-exact

@clerk/ui

npm i @clerk/ui@1.24.2-snapshot.v20260703092711 --save-exact

@clerk/upgrade

npm i @clerk/upgrade@2.0.5-snapshot.v20260703092711 --save-exact

@clerk/vue

npm i @clerk/vue@2.4.11-snapshot.v20260703092711 --save-exact

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/shared/src/utils/timeLimit.ts (1)

1-25: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Missing JSDoc on exported public utility.

timeLimit is a new shared, publicly exported utility (via @clerk/shared/utils) but has no JSDoc. The repo guideline requires documenting functions with @param/@returns/@throws, especially for public APIs.

📝 Suggested JSDoc
+/**
+ * Races a value (or promise) against a timeout, rejecting if the timeout elapses first.
+ *
+ * `@param` value - The value or promise to race against the timeout.
+ * `@param` ms - The timeout duration in milliseconds.
+ * `@param` abortController - Optional controller aborted (with no reason) when the timeout wins the race.
+ * `@returns` A promise that resolves with `value` or rejects with a timeout `Error`.
+ * `@throws` {Error} When `ms` elapses before `value` settles.
+ */
 export function timeLimit<T>(
   value: T | PromiseLike<T>,
   ms: number,
   abortController?: Pick<AbortController, 'abort'>,
 ): Promise<T> {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/shared/src/utils/timeLimit.ts` around lines 1 - 25, The exported
public utility timeLimit lacks the required JSDoc documentation; add a doc
comment directly above the timeLimit function that explains its timeout behavior
and includes `@param` entries for value, ms, and abortController, plus `@returns`
for the raced promise and `@throws` only if applicable. Keep the documentation
aligned with the function’s behavior in timeLimit so it is usable as a shared
`@clerk/shared/utils` API.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/shared/src/utils/timeLimit.ts`:
- Around line 1-25: The exported public utility timeLimit lacks the required
JSDoc documentation; add a doc comment directly above the timeLimit function
that explains its timeout behavior and includes `@param` entries for value, ms,
and abortController, plus `@returns` for the raced promise and `@throws` only if
applicable. Keep the documentation aligned with the function’s behavior in
timeLimit so it is usable as a shared `@clerk/shared/utils` API.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Repository UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: c6acc78a-1d98-48f6-8e80-ade33e212ddc

📥 Commits

Reviewing files that changed from the base of the PR and between e84be3a and fd33d8a.

📒 Files selected for processing (6)
  • packages/clerk-js/src/core/__tests__/clerk.test.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/resources/Base.ts
  • packages/clerk-js/src/core/resources/Client.ts
  • packages/shared/src/utils/__tests__/timeLimit.test.ts
  • packages/shared/src/utils/timeLimit.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/clerk-js/src/core/resources/Client.ts
  • packages/clerk-js/src/core/clerk.ts
  • packages/clerk-js/src/core/tests/clerk.test.ts

Why:
Follow-up fixes on the fail-fast slow-origin load path. The background /client
retry could overwrite newer client state (a sign-out, or a mutation's
piggybacked client) with a stale response, because Client.fetch() mutates the
shared Client singleton in place before the generation guard runs. A throwing
updateClient listener during degraded recovery could skip the poller restart
and leave session-token refresh stopped until a full page reload. The timeLimit
timeout error was passed as the AbortSignal reason, so a host fetch wrapper
reading signal.reason could observe an internal Clerk error. The new fetch
option `signal` also collided with Clerk's existing reactive signals concept.

What changed:
The background /client retry now reads raw JSON via Client._fetch and applies
fromJSON only inside the generation check, so a superseded response never
mutates the shared client. Degraded recovery wraps the identity rebuild in
try/finally so startPollingForToken always runs. timeLimit aborts with no
reason; the descriptive error stays only on the promise rejection. Renamed the
fetch option to `abortSignal` and timeLimit's parameter to `abortController` -
both are new on this branch, so there is no compatibility impact.
Why:
The degraded-load recovery carried a client-generation counter to discard a
background /client response that raced a newer client update. It added an
instance field, an increment in the hot updateClient path, and a low-level
fetch that reached past the resource API. Dropped it: the background retry now
applies the /client response directly, and the rare stale overwrite it guarded
against is self-correcting via the poller. Also collapsed the mint fallback so
recovery derives one client from a single jwt (minted, or the cookie jwt on
mint failure) rather than branching on a separate localClient.
…d retry

The "fails fast when the client fetch hangs" test made every mocked fetch hang,
including the background /client retry, which left a forever-pending promise
that stalled the test under CI. Scope the hang to the primary fetch and let the
retry resolve. Also inline the session-cookie read at its use sites in the
recovery path instead of hoisting a variable.
Compute the recovery jwt once (minted when a session exists, else the cookie)
and build the client from it in a single place, instead of duplicating
updateClient(createClientFromJwt(...)) across both branches.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant