unionlabs/union · error · Error

No wallet available

Error message

No wallet available

What it means

Thrown in the BTCfi Step3 flow (app2) when preparing to sign a Babylon ownership message: the app takes the stored wallet name from cosmosStore.connectedWallet and looks up the injected provider via window[connectedWallet]; if that global is absent, there is no wallet API to call signArbitrary on and the Effect fails synchronously.

Solutions

  1. Reconnect the wallet (trigger the connect flow again) so connectedWallet matches a currently injected provider
  2. Verify the extension is installed and enabled for the site, then reload
  3. Clear stale connection state when window[connectedWallet] is missing instead of attempting to sign

Example fix

// before
const walletApi = cosmosStore.connectedWallet && (window as any)[cosmosStore.connectedWallet]

// after (gate the flow and re-connect on miss)
const walletApi = cosmosStore.connectedWallet && (window as any)[cosmosStore.connectedWallet]
if (!walletApi) {
  cosmosStore.connectedWallet = null // clear stale state
  await reconnect() // run the wallet-connect flow again
  return
}
Defensive patterns

Strategy: validation

Validate before calling

const walletName = cosmosStore.connectedWallet
const walletApi = walletName && (window as any)[walletName]
if (!walletApi || typeof walletApi.signArbitrary !== "function") {
  cosmosStore.connectedWallet = null // clear stale state
  // trigger the connect flow instead of signing
}

Type guard

const isInjectedCosmosWallet = (w: unknown): w is { signArbitrary: (chainId: string, signer: string, message: string) => Promise<unknown> } =>
  typeof w === "object" && w !== null && typeof (w as any).signArbitrary === "function"

Try / catch

try {
  // Effect pipeline already routes this via Effect.orElse; for plain code:
  await walletApi.signArbitrary("bbn-1", address, message)
} catch (error) {
  if ((error as Error).message === "No wallet available") {
    // prompt reconnect; do not retry until window global exists
  }
  throw error
}

Prevention

When it happens

Trigger: cosmosStore.connectedWallet is set (e.g. "keplr" or "leap") but the corresponding window global is missing: extension uninstalled/disabled, injection not finished at call time, the name restored from persisted state after a browser profile change, or a non-cosmos wallet was connected.

Common situations: User disabled the extension between sessions; page loaded and user clicked through before injection (race); localStorage/session persisted connectedWallet stale; mobile in-app browser without the extension.

Related errors


AI-assisted analysis of unionlabs/union@031785bb6d (2026-08-16). Data as JSON: /api/errors/c7131e9dec08400f. Report an issue: GitHub.

Appendix: source

Thrown at app2/src/routes/btcfi/step/Step3.svelte:107

  }

  if (!isEvmAddressValid) {
    return
  }

  const addressToUse = evmAddress.trim()
  isLoading = true

  const message =
    `I verify that I own this Babylon address and want to receive BTCfi rewards on Ethereum address: ${addressToUse}`

  runPromise(
    pipe(
      Effect.sync(() => {
        const walletApi = cosmosStore.connectedWallet
          && (window as any)[cosmosStore.connectedWallet]
        if (!walletApi) {
          throw new Error("No wallet available")
        }
        return walletApi
      }),
      Effect.flatMap((walletApi) =>
        Effect.tryPromise({
          try: () => {
            const chainId = "bbn-1"
            return walletApi.signArbitrary(chainId, walletAddress, message)
          },
          catch: (error) => new Error(`Failed to sign message: ${error}`),
        })
      ),
      Effect.flatMap((signature) =>
        verifyBTCFIWallet({
          bbnAddress: walletAddress,
          message,
          signature: JSON.stringify(signature),
          evmAddress: addressToUse,

View on GitHub (pinned to 031785bb6d)