LazorKit LogoLazorKit

Troubleshooting

Common errors, program error codes, and fixes for LazorKit SDKs.

A reference for the issues you'll hit most often. Every entry has symptoms, root cause, and a concrete fix. Every error class and program code is listed on Errors.

Enable debug logging first

Before anything else, set isDebug={true} on <LazorKitProvider> (React Native) to stream SDK logs to the console. For the React SDK, read useWallet().error and the browser console.

Section map

TopicSymptoms include
SetupCannot resolve module, ERESOLVE, Buffer is not defined, SSR crashes
Wallet addressFunds sent to the wrong address, balance stuck at 0
Passkey / WebAuthnNotAllowedError, "user denied permission", simulator issues, portal closed, a message signature that does not verify
Sending transactions3006, outcome unknown, "Already signing", deferred expiry
Sessions and kept keys3037 / 3038, KeyWalletMismatchError, createSession refusing its limits, a sign-out through wallet-adapter
Network / relayNetwork request failed, paymaster errors, RPC refusals
Deep linkingPortal never returns, redirect drops signature
v1 and v24018, 4008, 3005, migrated wallets, cluster detection
Program errors0xbd0 ActionSolLimitExceeded, auth payload, replay

Setup

Cannot resolve module '@solana/web3.js'

@solana/web3.js is a peer dependency.
npm install @solana/web3.js

Then restart your bundler / Metro to clear module caches.

npm ERESOLVE installing @lazorkit/wallet

@lazorkit/wallet 3.x needs @solana/web3.js 1.99 or later (its peer @solana/wallet-adapter-base 0.9.28 does). An app pinned to 1.98.x cannot resolve it:

npm install @solana/web3.js@^1.99.0

peer @solana/kit@"^5.0" from @solana-program/token@0.9.0 in an app on @solana/kit 6, 7 or 8: 3.3.0 and earlier declared @solana/kit ^5, @solana/kora and @solana-program/token as peers, though nothing in the wallet loads them. 3.3.1 dropped them; install it instead of using --legacy-peer-deps:

npm install @lazorkit/wallet@^3.3.1

Buffer is not defined

@lazorkit/wallet 3.0.2 and later, and @lazorkit/sdk-legacy 0.3.2 / 1.x, need no global Buffer. If another library in your app does, polyfill it per environment:

At the very top of your entry file (app/_layout.tsx, index.js, App.tsx):

import 'react-native-get-random-values';
import 'react-native-url-polyfill/auto';
import { Buffer } from 'buffer';
global.Buffer = global.Buffer || Buffer;

In your client providers file:

app/providers.tsx
'use client';
import { Buffer } from 'buffer';
if (typeof window !== 'undefined') (window as any).Buffer ??= Buffer;
vite.config.ts
import { defineConfig } from 'vite';
import { nodePolyfills } from 'vite-plugin-node-polyfills';
export default defineConfig({ plugins: [nodePolyfills()] });

Install the buffer package and register it once:

src/polyfills.ts
import { Buffer } from 'buffer';
(window as any).Buffer = (window as any).Buffer ?? Buffer;

Import ./polyfills before everything else in src/index.tsx.

"Window is not defined" / SSR crashes

The React SDK reads localStorage, opens the portal, and calls navigator.credentials — all client-only. In Next.js App Router, mount the provider inside a "use client" boundary:

app/providers.tsx
'use client';
import { LazorkitProvider } from '@lazorkit/wallet';
export function Providers({ children }: { children: React.ReactNode }) {
  return <LazorkitProvider>{children}</LazorkitProvider>;
}

Never call useWallet from a server component.

Random-values crash (Expo)

If crypto.getRandomValues throws at startup, react-native-get-random-values was not imported first. Make import 'react-native-get-random-values'; the first line of your entry file. The adapter needs it for Keypair.generate() and ownership challenges.


Wallet address

Which address do I show and fund?

The vault. The SDKs name it differently:

SDKVault (show, fund, fromPubkey)Wallet PDA (internal — never send funds)
React (@lazorkit/wallet)vaultPubkey, wallet.vaultPdasmartWalletPubkey, wallet.smartWallet
React Native (@lazorkit/wallet-mobile-adapter)smartWalletPubkey (= vaultPubkey), wallet.smartWalletwalletPdaPubkey, wallet.walletPda
Wallet adapter / Wallet StandardpublicKey (3.0.0+)—
@lazorkit/sdk-legacy, @lazorkit/sdkvaultPdawalletPda

vaultPubkey is the vault on both UI SDKs.

Funds sent to the wallet PDA are lost

On the web SDK, smartWalletPubkey / wallet.smartWallet is the wallet PDA. Nothing can sign for it, so SOL or tokens sent there cannot be recovered. Show and fund vaultPubkey.

"Insufficient funds" on the vault

A transaction that spends SOL from the vault needs the vault to hold enough. Fund it by sending SOL to the vault:

import { useWallet } from '@lazorkit/wallet'; // or '@lazorkit/wallet-mobile-adapter'

const { vaultPubkey } = useWallet();
console.log('receive SOL at:', vaultPubkey?.toBase58()); // the vault, on web and mobile

Balance shows 0

You are reading the wallet PDA instead of the vault, or deriving the vault yourself. findVaultPda derives a v2 address at the v2 mainnet id, wrong for a v1 wallet and for devnet. Read the vault the SDK stored:

// before: await connection.getBalance(findVaultPda(walletPda)[0])
await connection.getBalance(vaultPubkey!);

A React Native app that upgraded from adapter 1.x had smartWallet holding the wallet PDA; the store migrates persisted records to the vault on load (its own storage format, v0 → v1).


Passkey / WebAuthn

NotAllowedError: The request is not allowed by the user agent or platform

The standard WebAuthn denial. Covers user cancellation, missing biometric, and environments without a platform authenticator.

Likely causes

  • User tapped Cancel on the Face ID / Touch ID sheet.
  • Device has no biometric or screen lock enrolled — there's no platform authenticator to prompt.
  • iOS Simulator: Face ID not enrolled (Features → Face ID → Enrolled).
  • Android Emulator without Play Services, or without a screen lock / biometric set.
  • iOS < 16 or Android < 9 — passkey-class authenticators are unavailable.

Fix checklist

  • Test on a physical device with Face ID / Touch ID / screen lock enrolled.
  • For iOS simulators, enable Face ID enrollment in the Simulator menu.
  • For Android emulators, pick a Pixel image with Google Play + set a screen lock.
  • Ensure iCloud Keychain (iOS) or Google Password Manager (Android) is signed in — passkeys require the platform's credential store.
  • Open https://portal.lazor.sh directly in the device's browser to confirm passkey works outside your app.

PortalCancelledError

The user closed the portal — the dialog's X, Escape, a click outside, the popup (web), or dismissed the browser (mobile) — before it answered; or disconnect was called during connect. Nothing was signed. Treat it as a cancel. Since @lazorkit/wallet 3.0.0 / adapter 2.0.0 this is raised at once instead of after a 60-second timeout.

connect asks for one more signature

An existing passkey that has no wallet yet (made on another device or browser) does not reveal its public key on sign-in. Since 3.1.0 / 2.1.0, connect recovers the key from two of its signatures, which costs one extra prompt. If the user closes it, connect rejects with PortalCancelledError; if the signatures do not settle on one key, it throws an error that says so. Nothing is created in either case. See Wallet Confirmation.

WalletChooserNotShownError (iOS)

The built-in chooser is a React Native Modal and iOS shows one modal at a time. Mount <WalletChooser /> inside your own modal, or close it before connecting. See Wallet Confirmation › iOS.

A message signature does not verify

verifyWalletMessage resolves false when any of these differ from what the passkey signed, and verifySignedMessage likewise:

  • The signature was made by @lazorkit/wallet 3.3.0 / adapter 2.3.0 or earlier, over the message's own bytes. Since 3.3.1 / 2.3.1 the passkey signs signedMessageChallenge(message); ask the user to sign again.
  • message is not the exact string (or bytes) that was signed.
  • rpId is not the portal the passkey was created under (portal.lazor.sh by default), or origin, when you pass it, is not the portal's origin.
  • wallet or credentialId names another wallet or passkey, or the passkey is not an Owner of that wallet.
  • An adapter or Wallet Standard signature was passed as it came: it is UTF-8 JSON; pass JSON.parse(new TextDecoder().decode(signature)).

It rejects, rather than resolving false, when it cannot read the chain: use an RPC endpoint that allows getProgramAccounts. Never fall back to the deprecated verifyMessage / verifySignatureBrowser: they do not check which message was signed.

rpId mismatch / "credentials don't match"

rpId is baked into every passkey — and into the wallet

A passkey is bound to a specific Relying Party ID, and each passkey authority stores sha256(rpId) on chain. If you change rpId after a user has registered, their existing passkeys won't match and every connect will fail.

Keep rpId stable across environments (or scope it per-env and migrate users explicitly). The default portal.lazor.sh works for most integrations. Moving users to a new rpId means adding their new passkey as an authority with a transaction signed by the old one.

Portal opens but closes immediately

Usually a portal URL mismatch — the WebBrowser session returns dismiss without a redirect payload. Check:

  • portalUrl resolves over HTTPS (WebAuthn requires a secure context).
  • Device has network connectivity to portal.lazor.sh.
  • Portal page loads without ad-blocker interference.

Sending transactions

Since @lazorkit/wallet 3.1.0 / adapter 2.1.0, every send resolves only once its transaction is confirmed. See React / React Native › Sending transactions.

SignatureReusedError / 0xbbe (3006)

The passkey signed a counter that another transaction of the same passkey had already used. Causes, most likely first:

  • The same passkey signing twice at once — two tabs, two devices, or an app and a wallet. The SDK serialises calls within a page, not across them.
  • Back-to-back sends on an old SDK. Before wallet 3.1.0 / adapter 2.1.0 / sdk-legacy 1.3.0, the second challenge could read the counter before the first transaction executed. Upgrade; with @lazorkit/sdk-legacy, pass minContextSlot (Passkey signing).
  • Not LazorKit's at all: an inner Anchor program's AccountNotMutable is also 3006. The first Program <id> failed log line names the program; the UI SDKs check it and report such a failure as it is.

The signature can never become valid. The SDK does not resend it or open a new prompt on its own: ask the user to approve again.

TransactionOutcomeUnknownError / ConfirmationTimeoutError

Whether the transaction landed is not known — the paymaster's answer was lost, or no outcome within two minutes. Do not resend blindly: check error.signature (when set), or the balance or state the transaction would change, first.

PreviousTransactionPendingError

Nothing was signed or sent: the passkey's previous transaction still has no known outcome, and a new signature could reuse its counter. Wait, then try again.

"Already signing" (web) / SigningError (mobile)

Another call is still running: isSigning stays true until the transaction is confirmed. Disable buttons while isSigning, and await one send before starting the next. A send started from an action's onSuccess runs (web 3.3.0+, mobile 2.2.0+); on web 3.2.1 and earlier it was refused this way too.

DeferredExpiredError / 0xbc6 (3014)

TX2 of a deferred execution came after its window (expiryOffset slots after TX1; default 1500 in the UI SDKs, 300 in @lazorkit/sdk-legacy). Nothing ran; ask the user to approve again. If you split TX1 and TX2, send TX2 sooner or pass a larger expiryOffset (at most 9000). An inner program's 3014 (Anchor's AccountNotAssociatedTokenAccount) is a different failure — usually a missing token account — and since 3.2.1 / 2.2.1 the SDKs report it as it came.

InvalidSignatureAge / 0xbbf (3007)

The challenge's slot is more than 150 slots old by the time the transaction runs — the user took too long to approve, or (with commitment: 'finalized') the slot was already old when the prompt opened. Prepare again.


Sessions and kept keys

UnlistedSolOutflowError / 0xbdd (3037)

"This session is not allowed to spend SOL" ("This key …" for a Delegate). The transaction would have lowered the wallet's SOL balance, and the session's limits (the Delegate's policy) name no SOL. Only the v2 program release that adds errors 3037 and 3038 returns it; that release is not deployed yet (Networks & versions). Nothing in the transaction ran, and the SDK does not resend it. Causes:

  • No SOL limit, and the transaction moves SOL. A session made with token limits only spends no SOL.
  • Rent. Creating an account the vault pays for (a recipient's token account, a swap's output account) lowers the vault's SOL. Give the session a SOL limit that covers the rent, or create the account first in an instruction the fee payer funds.
  • Wrapping SOL. Wrapping spends SOL; wSOL is a mint of its own.

Limits cannot be changed: revoke the session (remove the Delegate) and register one with a SOL limit (solPerTxMax, solLifetimeCap or solRecurring on web; an Actions.sol* action otherwise). See Session Keys › What a policy bounds.

UnlistedTokenOutflowError / 0xbde (3038)

"This session is not allowed to spend this token". A token would have left the wallet whose mint no limit names, from the same program release. A session made with SOL limits only, as every SpendingLimits session before @lazorkit/wallet 3.4.0, spends no token; wSOL counts as a token. Register a session whose limits name each mint it spends: spendingLimits.tokens on web, an Actions.token* action on mobile and in the contract SDKs.

A raw 3037 or 3038 whose logs name another program as the first to fail is that program's, not LazorKit's; isUnlistedSolOutflowError / isUnlistedTokenOutflowError tell them apart.

createSession throws before the prompt (web)

Since 3.4.0 createSession checks spendingLimits before anything is read or the passkey is asked. It throws on a tokens entry with no limit, or with a mint that is not an address; a mint named twice; an amount outside a u64; a window of 0 slots; more than 16 actions; or "spendingLimits makes N bytes of actions; at most 244 fit in the transaction beside the passkey's response". A SOL limit takes 19 bytes (solRecurring 43), a token's lifetimeCap or perTxMax 51, its recurring 75: drop a limit, or a mint. The same budget applies to a Delegate policy and to a React Native session's actions, but there nothing checks it before the prompt, and a policy that does not fit fails after the user approved.

createSession returns the session my sessionKey already had (web)

For a sessionKey of your own that already has a session, createSession resolves with that session as it was made, whatever limits you pass: the program does not create a second session for the same key. To change its limits, revoke it (revokeSession({ sessionPda })) and register it again, or register a new key. Since 3.4.0 the limits are checked first, so a call without valid spendingLimits (and not unrestricted) throws instead of resolving.

KeyWalletMismatchError with reason: 'disconnected' (web)

The wallet was disconnected while this signAndSendWithSession or signAndSendWithAuthority was running, by disconnect(), wallet-adapter or the Wallet Standard, and the same wallet is connected again by now. A send started before a sign-out is not finished after it (3.4.0). Nothing was sent: the message ends "Nothing was signed or sent." or, when the key had already signed, "The transaction it had signed was not sent." Send again. reason: 'no-wallet' is the same refusal with no wallet connected. When an earlier attempt of the same send may have reached the chain, the send rejects with TransactionOutcomeUnknownError instead: check before sending again.

useWallet() shows no wallet after a wallet-adapter Disconnect

Since 3.4.0 LazorkitWalletAdapter.disconnect() and standard:disconnect disconnect the React SDK's store too, as its own disconnect() does: the stored wallet is shared. Connect again. See Wallet Standard › Disconnecting.


Network / relay

TypeError: Network request failed

A fetch call inside the SDK failed — typically getPayerSigner or signAndSendTransaction against the paymaster.

Most common cause (React Native): the paymasterUrl resolved to undefined because your env var wasn't set. Guard the env var, or rely on the SDK default:

import { LazorKitProvider } from '@lazorkit/wallet-mobile-adapter';

const PAYMASTER_URL = process.env.EXPO_PUBLIC_PAYMASTER_URL;

<LazorKitProvider
  {...(PAYMASTER_URL ? { configPaymaster: { paymasterUrl: PAYMASTER_URL } } : {})}
>
  <App />
</LazorKitProvider>

PaymasterError

The paymaster refused the request. code and data carry its JSON-RPC error; httpStatus a non-success HTTP status.

  • Unauthorized / 401: the relayer requires an API key (apiKey, sent as x-api-key) or another check (reCAPTCHA) your app does not pass.
  • Program not allowed: the relayer's allowed_programs lacks the LazorKit program for this wallet's protocol, or the Secp256r1 precompile. LazorKit's v2 relayer does not sponsor v1 wallets: set v1PaymasterConfig / v1ConfigPaymaster.
  • Simulation failed: the instruction set reverts. data usually holds the program logs; see Errors › Program error codes.

getProgramAccounts refused, or connect fails reading the chain

A fresh connect looks the passkey's wallets up with getProgramAccounts, which many public or rate-limited RPC endpoints refuse. Use an RPC that allows it. A failed read fails connect; it is never taken as "no wallet", so no wallet is created by mistake.

MinContextSlotNotReachedError

A challenge read with minContextSlot found no RPC node at that slot within 10 s (30 s at finalized). The endpoint is behind: retry, or use an RPC that has caught up.


Deep linking (React Native only)

Portal redirects, but the app never wakes up

The portal sent the signature back, but the OS didn't route it to your app.

Fix

  • Confirm your scheme is registered in app.json / Info.plist / AndroidManifest.xml.
  • The redirectUrl passed into every mutation method must match a registered scheme exactly — case-sensitive, no trailing slash surprises.
  • In Expo dev with Expo Go, use exp://<your-tunnel> or exp://localhost:8081.
  • On Android, check adb logcat | grep -i intent while the redirect fires — if the intent never reaches your app, the scheme isn't registered correctly.
  • On Android, another installed app may also claim your custom scheme. Prefer a verified App Link as redirectUrl.

Redirect URL arrives without signature fields

The URL came back like myapp://cb?type=error&error=.... The adapter rejects with a LazorKitError whose code is 'PORTAL_ERROR' and whose message is the portal's text. Common triggers:

  • NotAllowedError from WebAuthn (see above) — user cancelled or environment denied.
  • Portal timeout — user took too long to authenticate.
  • Portal received an invalid challenge payload — inspect the SDK debug logs.

Handle it like any rejection, or in the onFail callback:

await signAndSendTransaction(
  { instructions: [ix] },
  {
    redirectUrl: 'myapp://cb',
    onFail: (err) => console.warn('portal failed:', err.message),
  },
);

v1 and v2

V1WalletRetiredError / 0xfb2 (4018)

The wallet is on LazorKit v1, and v1 has been retired to its sunset binary. The funds are safe; the wallet must move to v2 with migrateV1Wallet or the LazorKit migration page. See Migrating from v1.

V1WalletMigratedError

The stored v1 wallet was migrated (or closed). Call connect again; it finds the passkey's v2 wallet.

FeeAccountsRequired / 0xfa8 (4008)

A v2 CreateWallet, Execute or ExecuteDeferred came without the four protocol-fee accounts. v2 requires them even when no fee is charged. You are using an SDK older than the v2 release, or building instructions by hand: use the LazorKitClient methods of @lazorkit/sdk-legacy 1.x.

InvalidMessageHash / 0xbbd (3005) on every passkey transaction

The v2 program names the wallet in the passkey challenge. A client that builds the challenge without it (an SDK from before that change, or a hand-built flow without the wallet argument) fails every passkey transaction with 3005. Upgrade to the current packages.

LazorKitClient: cannot infer program ID from RPC endpoint

@lazorkit/sdk-legacy reads the cluster from the RPC URL and throws when the URL names none. Pass the program id: new LazorKitClient(connection, PROGRAM_ID_DEVNET). The UI SDKs take the cluster prop instead (they treat an unknown URL as mainnet).

A new user cannot get a wallet on mainnet

New wallets are created on protocol v2, whose mainnet program is not deployed yet. See Networks & versions.


Precompile errors (0x0 – 0x5)

Low-numbered error codes are from Solana's native precompile programs, not LazorKit. The most common one you'll see with passkey signing:

HexSource programCause
0x2Secp256r1SigVerify111...InvalidSignature — the precompile couldn't verify the r/s/pubkey/message tuple.

Debugging 0x2 InvalidSignature

The secp256r1 precompile verifies sig against (publicKey, authenticatorData ‖ sha256(clientDataJSON)). Any of these being wrong produces 0x2:

  • Stale cached pubkey. The client signed with a publicKeyBytes that doesn't match the on-chain authority — for example after cross-device passkey use. The current SDKs read the key from the on-chain authority before every passkey transaction; upgrade, and disconnect + connect once to refresh the stored record.

  • A signature not in the program's format. The precompile takes a 64-byte r‖s with a low S. The browser returns DER; convert it (see Passkey signing).

  • authenticatorData from the wrong authenticator. Make sure the portal returns bytes from the same credential the user just touched, not a cached response.

  • clientDataJSON tampered. The program hashes the raw bytes; any proxy that rewrites headers or trims whitespace breaks the signature.

Log the prepared challenge, the raw clientDataJSON, and the compressed pubkey — comparing them against the on-chain authority usually pinpoints the desync in seconds.


Program errors (0xbd0, 0xbd1, …)

Custom program errors come back from Solana as hex codes inside error messages and logs (custom program error: 0xbd0), or as { "Custom": 3024 }. The full table — 3001–3038 and 4001–4018, with what each means — is on Errors › Program error codes. The ones you will meet most:

HexDecNameFix
0xbd03024ActionSolLimitExceededLifetime SOL cap spent. Revoke and create a new session.
0xbd13025ActionSolRecurringLimitExceededWindow cap hit. Wait for the reset.
0xbcd3021ActionProgramNotWhitelistedThe call targets a program outside the whitelist.
0xbc13009SessionExpiredCreate a new session.
0xbbe3006SignatureReusedSee above.
0xbc63014DeferredAuthorizationExpiredSee above.
0xbd93033DelegateRequiresPolicyPass a policy when adding ROLE_SPENDER.
0xbdd3037ActionUnlistedSolOutflowSee above.
0xbde3038ActionUnlistedTokenOutflowSee above.
0xfb24018RetiredDeploymentSee v1 and v2.

Check which program failed

Programs your instructions call can use the same numbers (Anchor's account errors are 3000–3017). The first log line Program <id> failed: custom program error: 0x… names the program; only when it is LazorKit's does this table apply.

Decoding helper

const match = String(err).match(/custom program error: 0x([0-9a-f]+)/i);
if (match) {
  const code = parseInt(match[1], 16);
  console.log('program error:', code);        // e.g. 3024
}

Both UI SDKs and @lazorkit/sdk-legacy also ship this helper:

import { extractErrorCode, errorFromCode } from '@lazorkit/wallet';
// or '@lazorkit/wallet-mobile-adapter' / '@lazorkit/sdk-legacy'

const code = extractErrorCode(err);            // 3024, or null
const name = code === null ? undefined : errorFromCode(code); // 'ActionSolLimitExceeded'

Session exhaustion pattern

0xbd0 / 0xbd1 / 0xbd3 all mean "session hit a spending limit". Because actions are immutable, the only way forward is to revoke and recreate (React Native shown):

import { Actions } from '@lazorkit/wallet-mobile-adapter';

await revokeSession({ sessionPda }, { redirectUrl });
const { sessionPda: newPda } = await createSession(
  {
    sessionKey: freshKp.publicKey,
    expiresAtSlot: currentSlot + 216_000n,
    actions: [Actions.solRecurringLimit({ limit: 2_000_000_000n, window: 216_000n })],
  },
  { redirectUrl },
);

Still stuck?

When filing an issue, include:

  1. SDK version (npm ls @lazorkit/wallet @lazorkit/wallet-mobile-adapter @lazorkit/sdk-legacy)
  2. Platform + OS version (iOS 17.4, Android 14, Chrome 130, etc.)
  3. Debug logs (isDebug={true})
  4. The exact redirectUrl / portalUrl / paymasterUrl used, and the cluster
  5. Program error code and transaction logs if applicable

On this page

Section mapSetupCannot resolve module '@solana/web3.js'npm ERESOLVE installing @lazorkit/walletBuffer is not defined"Window is not defined" / SSR crashesRandom-values crash (Expo)Wallet addressWhich address do I show and fund?"Insufficient funds" on the vaultBalance shows 0Passkey / WebAuthnNotAllowedError: The request is not allowed by the user agent or platformPortalCancelledErrorconnect asks for one more signatureWalletChooserNotShownError (iOS)A message signature does not verifyrpId mismatch / "credentials don't match"Portal opens but closes immediatelySending transactionsSignatureReusedError / 0xbbe (3006)TransactionOutcomeUnknownError / ConfirmationTimeoutErrorPreviousTransactionPendingError"Already signing" (web) / SigningError (mobile)DeferredExpiredError / 0xbc6 (3014)InvalidSignatureAge / 0xbbf (3007)Sessions and kept keysUnlistedSolOutflowError / 0xbdd (3037)UnlistedTokenOutflowError / 0xbde (3038)createSession throws before the prompt (web)createSession returns the session my sessionKey already had (web)KeyWalletMismatchError with reason: 'disconnected' (web)useWallet() shows no wallet after a wallet-adapter DisconnectNetwork / relayTypeError: Network request failedPaymasterErrorgetProgramAccounts refused, or connect fails reading the chainMinContextSlotNotReachedErrorDeep linking (React Native only)Portal redirects, but the app never wakes upRedirect URL arrives without signature fieldsv1 and v2V1WalletRetiredError / 0xfb2 (4018)V1WalletMigratedErrorFeeAccountsRequired / 0xfa8 (4008)InvalidMessageHash / 0xbbd (3005) on every passkey transactionLazorKitClient: cannot infer program ID from RPC endpointA new user cannot get a wallet on mainnetPrecompile errors (0x0 – 0x5)Debugging 0x2 InvalidSignatureProgram errors (0xbd0, 0xbd1, …)Decoding helperSession exhaustion patternStill stuck?