Troubleshooting
Start with the observed symptom. For API-specific error shapes and diagnostic fields, see Error Codes & Diagnostics.
Capture diagnostics first
Log the code and 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,
})
}Enable telemetry for support correlation. The SDK forwards one operationId
per operation to the dialog and 1auth API:
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 rejects non-HTTPS app origins. Plain http://localhost
works only with a development-mode provider you run yourself.
Serve the app over HTTPS on a real hostname. tailscale cert and
Caddy issue locally trusted certificates. A
self-signed certificate must be installed in the OS trust store; clicking
through Chrome's interstitial is not enough.
Requests to the 1auth API fail with a CORS error
1auth does not reject unknown origins at CORS. Check the failing network
request for a providerUrl typo, CSP omission, or proxy stripping headers
before changing app registration.
The dialog shows a warning icon and asks users to confirm they trust the site
Your origin is EXTERNAL, which adds a warning and trust confirmation. 1auth
returns TRUSTED, REGISTERED, or EXTERNAL in X-Origin-Tier; GET /api/cors/check reports it directly.
To become REGISTERED, verify the exact scheme and host. https://app.example.com
and https://www.app.example.com are distinct origins and both need registration.
Sign-in and passkeys
The user dismissed the system prompt
USER_CANCELLED or USER_REJECTED is deliberate. Preserve form state and let
the user retry; do not auto-retry, because browsers may suppress another
unprompted WebAuthn call.
The browser reports NotAllowedError and no passkey was created
The ceremony ended before credential creation. Common causes:
- 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 it reproduces with one passkey provider but not another on the same browser and OS, report the provider; that distinction is actionable.
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 another origin resolves
to another user and smart account. Check the registration/sign-in origin and
that rpId has not changed; see Per-App Accounts.
The account address changed unexpectedly
An address is derived from the app's account namespace, so it moves whenever
that namespace moves. Check 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. See
App-origin passkeys.
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),
})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
On-chain simulation rejected the passkey signature: the account signer diverged from this credential. Retrying with the same passkey will fail; use account recovery.
EXECUTE_FAILED
Submission or execution failed downstream. Log providerCode, traceId, and
simulationUrls; they identify the execution and its revert. Never expose these
developer diagnostics to users.
HASH_TIMEOUT or STATUS_TIMEOUT
The intent was accepted but not confirmed in the wait window. It has not
necessarily failed. Keep result.intentId, unblock the UI, and poll
client.getIntentStatus(intentId); treating timeout as failure causes duplicates.
NETWORK_ERROR
SDK-to-1auth transport failure. Retry after connectivity returns; if persistent,
check providerUrl requests for corporate proxy, ad-blocker, or CSP blocking.
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.