Developer docs

Documented against main @ bbedf98 (0.8.9-dev, 4 November 2026) · latest release v0.8.8

Overview

Errors and node readiness

Errors carry an EIP-1193-style code and usually a data.reason. Most failures fall into four groups, and only some are worth retrying.

On this page

Every rejection is an Error with code, message and, for most validation and node failures, data.reason.

async function call(method, params) {
  try {
    return await window.swarm.request({ method, params });
  } catch (err) {
    switch (err.code) {
      case 4001:  // the user said no
        throw err;
      case 4100:  // a grant is missing: data.reason says which
        await window.swarm.requestAccess();
        return window.swarm.request({ method, params });
      case 4900:  // node not available
        if (err.data?.reason === 'node-stopped' || err.data?.reason === 'node-not-ready') {
          await new Promise((r) => setTimeout(r, 3000));
          return window.swarm.request({ method, params });
        }
        throw err;
      default:
        throw err;
    }
  }
}

A real app should cap retries and back off; this only shows the shape.

The four groups#

The user decided (4001). Stop. Ask again only on a new user action. A declined manifest sheet stays declined until the page reloads, so tell the user to reload rather than calling requestAccess in a loop.

A grant is missing (4100). not_connected, feed_not_granted or messaging_not_granted. Call requestAccess, or the tier's first method, to show the prompt. A 4100 also tells you the user disconnected your origin.

The node is not ready (4900). data.reason says why:

  • node-stopped: the node is not running or not answering. It also happens for a short while right after the user unlocks, while Freedom restarts the node (#988). Retry with backoff.
  • node-not-ready: the node is still connecting, or a storage batch bought moments ago is not yet known to the network. Retry later.
  • ultra-light-mode, no-usable-stamps: the node cannot publish yet (no Gnosis Chain connection, or no storage with room for the write).

On a write method, all four of these also open Freedom's publish setup for the user, so tell them what is happening rather than retrying in a loop. Reads only ever report node-stopped.

A 4900 without data means the provider is disabled (Settings → Advanced → Enable Identity & Wallet is off) or the app's permission manifest could not be fetched. Check getCapabilities() up front: canPublish is false until the origin is connected and the node is ready.

The request is wrong (-32602). Fix the call. data.reason names the problem, sometimes with data.limit and data.actual. Two are retryable: rate_limited (wait for the 60 s read window, or retryAfterMs for new GSOC topics) and too_many_subscriptions (close one first).

-32603 is an internal error or a timeout and is usually transient. A request that timed out may still have completed on the node, and a response that arrives after the page navigated away is never delivered.

Timeouts#

window.swarm gives publish, feed, signing and messaging calls 300 s and everything else 60 s, prompt time included. A user who takes longer than 60 s on the connect prompt gets -32603 Request timed out even though the grant still lands; call requestAccess again to pick it up.

window.radicle uses 300 s for seeding and writes and 60 s otherwise. radicle_unseed currently gets only 60 s (#1001).

Radicle specifics#

window.radicle uses the same codes. Its 4900 reasons are profile-disabled (Radicle is off for this profile) and node-stopped; the node-status, access and disconnect methods still work while the node is stopped. Write validation failures are -32602 with reasons such as invalid_rid, invalid_title or payload_too_large. The full list is in Errors.

Full tables: window.swarm errors.