Skip to content

fix: handle createClient rejection so isLoading resolves#97

Open
nicknisi wants to merge 1 commit into
mainfrom
fix/catch-createclient-rejection
Open

fix: handle createClient rejection so isLoading resolves#97
nicknisi wants to merge 1 commit into
mainfrom
fix/catch-createclient-rejection

Conversation

@nicknisi
Copy link
Copy Markdown
Member

Summary

  • Adds a .catch() handler to the createClient(...).then(...) promise chain in AuthKitProvider
  • Without this, any createClient rejection (network failure, DNS error, hung custom domain, misconfigured apiHostname) leaves isLoading stuck at true forever
  • Apps can now render their unauthenticated state instead of showing a loading spinner indefinitely

Surfaced by VectorShift after an environment promotion where a custom auth domain wasn't bootstrapped — their app showed a permanent loading state because isLoading never resolved.

Related: workos/authkit-js#117 (post-sleep lock timeout), workos/authkit-js (fetch timeout PR)

Test plan

  • npm run build passes (tsup — CJS, ESM, DTS)
  • Verify in a React app: when createClient rejects (e.g., invalid clientId, unreachable apiHostname), isLoading transitions to false and the app renders unauthenticated UI
  • Verify happy path is unaffected — normal initialization still sets isLoading: false with a valid user

Without a .catch() on the createClient promise chain, any rejection
(network failure, DNS error, hung custom domain) leaves isLoading
stuck at true forever, rendering apps unable to show their
unauthenticated state.
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented May 15, 2026

Greptile Summary

Fixes an edge case where createClient rejections (network failure, bad apiHostname, DNS errors) left isLoading permanently true by chaining a .catch() that transitions to isLoading: false.

  • Error handler scope: the .catch() sits at the end of .then(async ...).catch(...), so it intercepts exceptions from anywhere in the promise chain — including bugs thrown inside the success callback — not just createClient rejections. This could silently mask a success-path error.
  • No error surfacing: the caught error is discarded with no logging and no error state exposed to consumers, making production failures hard to diagnose.

Confidence Score: 4/5

The core fix is correct and resolves a real stuck-loading bug; the two concerns are about error handler scope and observability rather than correctness of the happy path.

The change achieves its goal — isLoading now resolves on createClient failure. The handler's broad catch scope (covering the entire .then() chain) and the silent discard of the error are worth addressing before shipping, but neither introduces a regression on the success path.

src/provider.tsx — specifically the .catch() handler scope and the absence of any error logging or state.

Important Files Changed

Filename Overview
src/provider.tsx Adds .catch() to the createClient promise chain so isLoading resolves on rejection; the handler silently discards the error and is broad enough to also catch exceptions thrown inside the success callback.

Sequence Diagram

sequenceDiagram
    participant App
    participant AuthKitProvider
    participant createClient

    App->>AuthKitProvider: mount (clientId, apiHostname, ...)
    AuthKitProvider->>AuthKitProvider: setState(initialState) [isLoading: true]
    AuthKitProvider->>createClient: createClient(clientId, options)

    alt createClient resolves
        createClient-->>AuthKitProvider: client
        AuthKitProvider->>AuthKitProvider: setClient(boundMethods)
        AuthKitProvider->>AuthKitProvider: "setState({ isLoading: false, user })"
        AuthKitProvider-->>App: renders authenticated UI
    else createClient rejects (NEW)
        createClient-->>AuthKitProvider: Error (network/DNS/timeout)
        AuthKitProvider->>AuthKitProvider: ".catch() → setState({ isLoading: false })"
        Note over AuthKitProvider: error is silently discarded
        AuthKitProvider-->>App: renders unauthenticated UI
    else error thrown inside .then() handler (NEW, unintended)
        AuthKitProvider->>AuthKitProvider: exception in success callback
        AuthKitProvider->>AuthKitProvider: ".catch() → setState({ isLoading: false })"
        Note over AuthKitProvider: success-path bug silently masked
        AuthKitProvider-->>App: renders unauthenticated UI (wrong)
    end
Loading

Comments Outside Diff (1)

  1. src/provider.tsx, line 83-99 (link)

    P2 .catch() also catches errors thrown inside the .then() success handler

    Because .catch() is chained after .then(), it intercepts not only createClient rejections but also any exception thrown inside the async (client) => { ... } callback — for example, if a bound method or setState call were to throw. That would silently transition to the unauthenticated state even when the client was created successfully. Using the two-argument form of .then(onFulfilled, onRejected) would scope the handler strictly to createClient rejections.

Reviews (1): Last reviewed commit: "fix: handle createClient rejection so is..." | Re-trigger Greptile

Copy link
Copy Markdown

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 2 additional findings.

Open in Devin Review

Comment thread src/provider.tsx
Comment on lines +97 to 99
.catch(() => {
setState((prev) => ({ ...prev, isLoading: false }));
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Silent error swallowing makes failures invisible

The .catch() handler discards the rejection entirely — no logging, no exposed error state. When createClient fails due to a misconfigured apiHostname, DNS error, or network timeout, the app silently moves to the unauthenticated state with zero diagnostic signal. In production this makes the root cause nearly impossible to identify without additional instrumentation.

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant