Skip to main content
Never paste private keys, the issuer seed, access keys or recovery codes into issues or shared logs.

First checks

Deployment

The service’s PORT variable and the domain’s target port differ. Set PORT explicitly (8787 for relay-1, 8788 for relay-2, 8790 for the OHTTP relay) and make the domain’s target port the same.
Railway mounts volumes as root and the image runs as a normal user. Add RAILWAY_RUN_UID=0, or remove the volume and ISSUER_QUOTA_FILE (quotas then reset on restart).
The relayer found no key. Set RELAYER_PRIVATE_KEY, or RELAYER_1_PRIVATE_KEY when no RELAYER_KEY_VAR is given, or point RELAYER_KEY_VAR at the variable that holds it.
ISSUER=on needs ISSUER_RSA_SEED as 0x followed by 64 hex characters. npm run setup generates one.
Fund the relayer’s address (shown on /healthz and /v1/info) from the faucet or another wallet.
The same wallet is running in two places (another relayer, npm run dev:relayers, a test). Stop the other copy. One wallet, one process.

In the app

No relayers were given and no live heartbeat exists. Start a relayer, or pass relayers.
Every configured or discovered relayer failed /v1/info. Check /healthz. In a browser on an https page, http:// relayers are blocked as mixed content: relayers must have https URLs, and RELAYER_PUBLIC_URL must be that https URL.
The message lists each relayer’s reason:
  • relayer refused: 400 …: the record broke a rule (see the next section);
  • relayer refused: 402 …: the relayer requires tokens and none was sent;
  • relayer refused: 403 …: the token was rejected (spent, wrong day, unknown issuer key);
  • relayer refused: 429 … or 503 …: rate limit or full queue, try again or use another relayer;
  • timed out waiting for inclusion: the relayer’s queue stopped moving;
  • no batch of N formed in time; not written: minAnonymity was not reached within maxHoldMs;
  • … misbehaved: …: the relayer tampered with or misdirected the record; it is marked bad.
strict: true is set and check() raised warnings. Coarsen the attributes (see fingerprinting) or drop strict.
The Arkiv node rejects uppercase attribute names, and one bad name would fail every record in a batch, so the client refuses it first. Rename minBatch to minbatch.
Lifetimes are rounded up to 1 hour, 1 day, 7 days, 30 days or 90 days; longer is refused. Also, expires must be relative (ExpirationTime.fromDays(…)), not an absolute block or date.
The payload plus envelope does not fit in 16 KB. Store less, or split it into several records.
proveOwnership could not find the record among yours. It must be a record written by this wallet in this app, and still live.
The wallet signs non-deterministically (some MPC, passkey or hardware setups). Use random-root mode with a recovery code (KeyVault.random, WorkerVault.random).
Smart-contract wallets are not supported in this version. Use an externally owned account.

Tokens

Reading

Last modified on October 12, 2026