Troubleshooting
errors and their fixes.
Messages you may see, what they mean and what to do, grouped by where they appear. Tip: paste the exact message or error code into the search box. API errors have the shape {"error":{"code":"…","message":"…"}}.
01Install and build
pnpm setup does something unexpected
pnpm setup and pnpm licenses are pnpm built-ins. Use pnpm run setup and pnpm run licenses.
better-sqlite3 fails to install
No prebuilt binary for your platform and no compiler. Install your OS build tools (Xcode Command Line Tools on macOS, build-essential and python3 on Debian/Ubuntu), then run pnpm install again. Use Node 22.
pnpm run build stops at the contract
Hardhat downloads the solc compiler on first use and needs internet access. If you only need the server, build it alone: pnpm --filter "@mikodes-checkout/server..." build.
build:web prints "looks like a secret and will be public in the bundle"
A VITE_* variable in your shell has key, secret, token or password in its name. Remove it: every VITE_* value ships to browsers.
02Server start
invalid configuration: …
The server validates the env file and names every wrong value, for example OPERATOR_ADMIN_KEY must be at least 32 characters, OPERATOR_SOLANA_ADDRESS is not a valid Solana address or MAIL_FROM is required when SMTP_URL is set. Fix the line and start again.
BASE_ROUTER_ADDRESS is required when BASE_RPC_URL is set
Deploy the router first (how), or leave BASE_RPC_URL empty.
A chain is missing from /health
Its RPC URL is empty or invalid. The first log lines say which chains are disabled and why.
Two servers on one database
Not supported: webhooks would be delivered twice. Run exactly one process per SQLite file.
03Operator console
/admin returns 404
On the checkout domain this is intended: the console is only on the admin domain. On the admin domain, ADMIN_SECRET_KEY is empty, so the console is not mounted. Set it (openssl rand -hex 32) and restart.
I lost the setup code
Restart the server: while no owner exists, every start prints a new one-time code in the log.
"The setup code is wrong"
Use the code from the latest start; each restart replaces it.
Product pages (/ops) redirect to /admin or show nothing
You are not signed in to the console, or apps/ops/dist was not built. Build @mikodes-checkout/ops (or set OPS_UI_DIR) and restart.
operator_disabled
OPERATOR_ADMIN_KEY is not set, so every /v1/operator endpoint is off.
403 on the admin domain
Your IP is not in ADMIN_ALLOW_IPS. Behind a CDN or Docker's userland proxy, Caddy may not see real client IPs.
Telegram or SMTP stored values are unreadable after a change
ADMIN_SECRET_KEY changed. Re-enter the secrets under Integrations.
04Checkout and payments
Wallet pay fails at "prepare" with rpc_error
The server cannot reach your Solana RPC or is rate-limited. Check the provider; switch to a second one and restart. Payments made meanwhile are still found later.
Exchange pay is not offered
The merchant's fee credit is below the invoice fee, or Exchange pay is off in Console → Checkout, or exchange pay is switched off for that merchant.
The invoice stays confirming
A transaction was seen and the server waits for finality (on Base, BASE_CONFIRMATIONS blocks, default 12). A seen transaction keeps the invoice confirming past its expiry; this is intended.
Underpaid or overpaid
The amount received differs from the invoice. It goes to review and is never treated as paid automatically. The merchant can refund from the portal.
The payer says the payment link does not work
A link that was mistyped, deleted or has passed its retention window resolves to nothing. The payer gets a dead end that names no order, no amount and no merchant, and the fix is a fresh link from the store rather than an edit to the old one.

chain_not_enabled when creating an invoice
That chain has no RPC on the server, or it is switched off in Console → Chains & assets.
maintenance (503) when creating an invoice
Maintenance mode is on in Console → Access. Open invoices still settle.
topup_not_configured
No operator fee wallet is set for that chain, so there is no address to send fee credit to. Set OPERATOR_SOLANA_ADDRESS / OPERATOR_BASE_ADDRESS.
The browser shows a CORS error
Add the page's origin to CORS_ORIGINS (comma-separated, no trailing slash) and restart. Behind the provided Caddyfile the apps use the same origin and need no CORS entry.
05Webhooks
Deliveries to localhost or a private IP fail
They are refused on purpose (SSRF protection). For local development only, set WEBHOOK_ALLOW_PRIVATE=true. Never in production.
The merchant says the signature does not match
The receiver must compute HMAC-SHA256 over "<t>.<raw body>" with the current webhook secret, using the raw bytes, not re-serialised JSON. After a secret rotation the old secret stops working.
A delivery is a dead letter
It failed 8 times (default). Fix the endpoint, then retry it from the console or the portal (incident checklist).
06Demo instance
demo-reset.mjs wipe refuses to run
By design unless DEMO_MODE is true, every chain is a testnet, --yes-wipe-demo is given and every merchant is a demo merchant. It protects production databases.
Testers get 429 from devnet
The public devnet endpoint is rate-limited. Use your own devnet RPC for a busy demo.