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

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 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),
})

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 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.