Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Troubleshooting

This page is organized by what you observe. For the machine-readable codes themselves — where they appear in each API and which diagnostic fields travel with them — see Error Codes & Diagnostics.

Capture diagnostics first

Almost every entry below is faster to resolve with the error object in hand. Log the code and the normalized details on every failure path:

const result = await client.sendIntent({ accountAddress, targetChain: 8453, calls })
 
if (!result.success) {
  console.error('1auth failed', {
    code: result.error?.code,
    message: result.error?.message,
    providerCode: result.error?.details?.providerCode,
    traceId: result.error?.details?.traceId,
    simulationUrls: result.error?.details?.simulationUrls,
  })
}

To correlate a failure with 1auth's own records, enable the telemetry bridge. The SDK generates one operationId per operation and forwards it to the dialog and the 1auth API, so quoting it in a support request lets us find the exact ceremony:

const client = new OneAuthClient({
  clientId: 'my-app',
  telemetry: {
    onEvent: (event) => {
      console.debug('[1auth]', event.name, event.operationId, event.outcome)
    },
  },
})

Setup and local development

The passkey prompt never appears on http://localhost

WebAuthn requires a secure context, and the hosted provider additionally rejects a non-HTTPS app origin, so plain http://localhost only completes the flow against a provider you run yourself in development mode.

Serve your development app over HTTPS on a real hostname. tailscale cert and Caddy both issue locally-trusted certificates without extra setup. A self-signed certificate only works if you install it into the OS trust store as well — Chrome blocks WebAuthn on any page whose certificate it does not trust, so clicking through the interstitial is not enough.

Requests to the 1auth API fail with a CORS error

1auth does not reject unknown origins at the CORS layer, so a browser CORS error points somewhere else: a providerUrl typo, a Content Security Policy that omits the provider origin, or a proxy stripping headers. Check the failing request in the network tab before changing your app registration.

The dialog shows a warning icon and asks users to confirm they trust the site

Your origin is resolving to the EXTERNAL tier, which adds a warning marker to the dialog header and an extra trust-confirmation step before signing. 1auth classifies every calling origin as TRUSTED, REGISTERED, or EXTERNAL and returns the result in the X-Origin-Tier response header — GET /api/cors/check reports it directly.

To reach REGISTERED, register the exact scheme-and-host you serve from as a verified domain for your app. https://app.example.com and https://www.app.example.com are different origins and each needs registering.

Nothing happens when a popup flow starts

POPUP_BLOCKED. The browser suppressed window.open, which happens whenever the call is not attributable to a user gesture. Call experimental_signWithPopup directly from a click handler with no awaited work in front of it, or use signWithModal instead — the modal has no popup to block.

experimental_signWithPopup is cross-origin-only. In app-origin mode it returns APP_ORIGIN_FLOW_UNSUPPORTED rather than opening anything.

Sign-in and passkeys

The user dismissed the system prompt

USER_CANCELLED or USER_REJECTED. These are deliberate user actions, not faults. Keep the user on the current screen, preserve any form state, and let them retry. Do not auto-retry: a second unprompted WebAuthn call is likely to be suppressed by the browser.

The browser reports NotAllowedError and no passkey was created

The ceremony was aborted before a credential existed. Common causes, in order of likelihood:

  • The user dismissed the picker, or the ceremony timed out.
  • A password-manager extension claimed the prompt and the user rejected its dialog. On Chrome this cancels the entire WebAuthn request rather than falling back to the platform authenticator — the user must retry and choose the device's built-in passkey.
  • The call was not close enough to the trusted click. Anything awaited between the click and the ceremony — a fetch, a quote refresh, a dynamic import — can cost the user-activation window.

If the failure reproduces on one passkey provider but not another on the same browser and OS, that is a real signal rather than user error — report it and name the provider. We record the authenticator on every credential creation and can match it against the failure.

Registration fails with "Passkey was created by <provider> instead of your device"

PASSWORD_MANAGER_CREDENTIAL. The deployment is configured to require a platform authenticator, and the credential came from a password manager's vault instead. The user should dismiss the password-manager prompt and pick the device's built-in passkey (Touch ID, Windows Hello, Android screen lock).

The user has a passkey but 1auth says there is no account

Accounts are namespaced by origin. The same passkey on a different app origin resolves to a different user and a different smart account — this is by design; see Per-App Accounts. Check that the origin the user registered on is the origin they are signing in from, and that your rpId configuration has not changed since registration.

The account address changed unexpectedly

An address is derived from the app's account namespace, so it moves whenever that namespace moves. Check the WebAuthn mode and webauthn.rpId first, then whether the app moved to a new hostname. Each of those selects a different passkey, signer, and account, and 1auth does not migrate accounts between namespaces — retrying in the other mode reaches a different account rather than recovering the original one. See Choose your WebAuthn mode.

WEBAUTHN_CONTEXT_MISMATCH

The WebAuthn context at verification did not match the context the challenge was issued under — origin, RP ID, expected origin, or app identity changed mid-ceremony. Check for:

  • A client that changes clientId or webauthn.rpId between starting and completing a flow.
  • A proxy or CDN rewriting Origin / Referer on the request to the 1auth API.
  • A ceremony started on one origin and completed on another, for example after a redirect between example.com and www.example.com.

Challenges are short-lived, so a stale one also surfaces here. Restart the flow before investigating further.

APP_ORIGIN_SESSION_REQUIRED

The call needs an authenticated app-origin session and there isn't one. Call client.authenticate() first, and use client.getSession() to check state before issuing signing or intent calls. Register onDisconnect on the client so your app clears in-memory auth state when the dialog invalidates a session:

const client = new OneAuthClient({
  clientId: 'my-app',
  onDisconnect: () => setAccount(null),
})

APP_ORIGIN_FLOW_UNSUPPORTED

The method exists but is cross-origin-only, and this client is in app-origin mode. experimental_signWithPopup, experimental_signWithRedirect, experimental_signWithEmbed, EOA wallet connections, and redirect callbacks are all cross-origin-only. Use signWithModal and the app-origin authentication flow instead.

Signing and transactions

PREPARE_FAILED

Quoting or pre-sign simulation rejected the intent. Read error.details.reason and error.details.candidateErrors before changing anything — the message names the constraint.

A No viable route found message almost always means the account has no balance to source from, not that the route is unsupported. Fund the account, or send a sponsored intent with no token requirements to verify the rest of the integration first.

INVALID_SIGNATURE

Execution simulation rejected the passkey signature on-chain. The account's on-chain signer has diverged from the passkey that just signed — a stale or rotated credential, or a passkey that never owned this account.

Retrying is futile; the same passkey fails identically every time. Route the user into account recovery to add a new passkey.

EXECUTE_FAILED

Submission or execution failed downstream. Log providerCode, traceId, and simulationUrls and include them in a support request — those three fields are what we need to trace the execution. simulationUrls links to the transaction simulation and is usually enough to identify a revert yourself.

Never render simulationUrls or providerCode to end users; they are developer diagnostics.

HASH_TIMEOUT or STATUS_TIMEOUT

The intent was accepted but did not confirm inside the wait window. It has not necessarily failed. Keep result.intentId, stop blocking the UI, and poll client.getIntentStatus(intentId) separately. Treating a timeout as a failure is the most common cause of duplicate transactions.

NETWORK_ERROR

Transport-level failure between the SDK and the 1auth API. Retry once connectivity is back. If it is persistent, check that requests to your configured providerUrl are not being blocked by a corporate proxy, an ad blocker, or a Content Security Policy that omits the provider origin.

Sponsorship and app credentials

Every user intent carries the app's JWT, so a sponsorship misconfiguration surfaces as an intent failure rather than a configuration warning. See Fee Sponsorship for the full setup.

MISSING_APP_CREDENTIALS

No sponsorship is configured on the client. Pass sponsorship to the OneAuthClient constructor — either the callback pair or the URL pair — before sending intents or querying assets.

SPONSORSHIP_FETCH_FAILED

Sponsorship is configured, but fetching a token failed. Read the error message before anything else — when your endpoint returns a JSON error or message field, the SDK folds it into the thrown error, so the server's own reason (missing env var, bad JWK, expired session) is already there.

If the message is unhelpful, call the endpoints directly:

  • The access-token endpoint must return { "token": "..." }.
  • The extension-token endpoint must accept POST with { "intentOp": "<json string>" } and return { "token": "..." }.

The usual causes are a missing RHINESTONE_JWT_PRIVATE_KEY on the server, a private key pasted as a multi-line JSON file rather than a single-line JSON string, or same-origin /api/sponsorship/* URLs configured in an app that has no server to host them.

APP_JWT_REQUIRED

The 1auth API received the request without a usable app access token. The token was missing, expired, or minted with credentials that do not match the app. Mint a fresh token and confirm RHINESTONE_INTEGRATOR_ID, RHINESTONE_PROJECT_ID, RHINESTONE_APP_ID, and RHINESTONE_KEY_ID all belong to the same app.

sendBatchIntent() returned success: false with nothing to go on

SendBatchIntentResult reports top-level failures through errorCode (a machine-readable string) and error (a message) — not a nested error.code object like the single-intent path. Read both:

const batch = await client.sendBatchIntent({ accountAddress, intents })
 
if (!batch.success) {
  console.error('1auth batch failed', {
    errorCode: batch.errorCode,
    error: batch.error,
    results: batch.results,
  })
}

Several failure paths populate neither field, returning only success: false with an empty results array — including a failed access-token fetch, a missing accountAddress, an empty intents array, and a dialog closed before it was ready. For those, the reason exists only in the SDK telemetry event, which is the practical reason to wire up telemetry.onEvent before debugging batches:

telemetry: {
  onEvent: (event) => {
    if (event.outcome === 'failure' || event.outcome === 'cancelled') {
      console.error('[1auth]', event.name, event.errorCode, event.errorMessage)
    }
  },
}

batch.prepare.failed carries SPONSORSHIP_FETCH_FAILED or MISSING_APP_CREDENTIALS; dialog.cancelled carries USER_CANCELLED.

Contacting support

Include all of these — each one narrows the search from thousands of ceremonies to one:

FieldWhere to get it
clientIdYour client configuration
App originThe exact scheme and host the user was on
SDK operationIdThe telemetry event's operationId — not result.operationId, which is the orchestrator's
traceIderror.details.traceId
providerCodeerror.details.providerCode
Intent IDresult.intentId, for intent failures
Account addressThe address passed to the failing call
Approximate timestampWith time zone
Browser, OS, and passkey providere.g. "Chrome 131 / macOS / iCloud Keychain"

The browser and passkey provider matter more than they look: a large share of WebAuthn failures are specific to one provider on one platform, and naming them lets us go straight to the matching telemetry.