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
clientIdorwebauthn.rpIdbetween starting and completing a flow. - A proxy or CDN rewriting
Origin/Refereron the request to the 1auth API. - A ceremony started on one origin and completed on another, for example after a
redirect between
example.comandwww.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
POSTwith{ "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:
| Field | Where to get it |
|---|---|
clientId | Your client configuration |
| App origin | The exact scheme and host the user was on |
SDK operationId | The telemetry event's operationId — not result.operationId, which is the orchestrator's |
traceId | error.details.traceId |
providerCode | error.details.providerCode |
| Intent ID | result.intentId, for intent failures |
| Account address | The address passed to the failing call |
| Approximate timestamp | With time zone |
| Browser, OS, and passkey provider | e.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.