Troubleshooting
The failure messages this integration actually produces — what each one means, which layer it comes from, and the fix that works rather than the one that looks obvious.
Most problems here are one of four things: the licence file, the credentials, the IP allowlist, or a rate-limit ban. The symptoms overlap, so work from the message rather than the symptom.
Start every investigation at Admin → Finance → Exchange Providers and press Verify Credentials. It builds a throwaway connection, loads markets and fetches your balance, and its message names the layer that failed.
Connection and credentials
No row in the exchange table has status = true. Either you never enabled
Binance, or enabling a different provider switched it off — only one provider
can be active, and enabling one disables the rest in the same transaction.
Fix it from Admin → System → Extensions → Exchanges
(/admin/system/extension?type=exchange).
The enable call returned 403 because lic/38650585.lic does not exist under the
project root. The check reads the disk, not the database, so a licence column
that says "Licensed" does not help.
This is the usual symptom after restoring a database into a fresh checkout, or
after a deployment that did not carry the lic folder across. Re-activate from
Admin → System → Extensions, or from
/admin/system/license?productId=38650585.
APP_BINANCE_API_KEY or APP_BINANCE_API_SECRET is absent or empty in the
environment the backend process started with. Three things produce this that
look like something else:
- The variables were added to
.envbut the process was never restarted. They are read at boot. - They were added to the wrong
.env— they belong in the project root file. - A typo in the name. The names are built at runtime from the provider alias, so a misspelling is indistinguishable from an absent variable.
After three failures the manager stops trying for thirty minutes. If you fixed it and nothing changed, restart the backend rather than waiting.
Binance rejected the signature. The key and secret are present but wrong — most often a truncated secret, or a secret from a key that has since been deleted.
Note that a key which is fine but whose IP allowlist does not include your server fails here too, because the rejection happens at the same layer. Prove the IP before you regenerate the key: see Creating the Binance API key.
HTTP 451. Binance refuses connections from your server's region. No credential change will help.
Configure an outbound proxy in the Settings tab of the exchange provider screen, test it before saving, and then add the proxy's IP to the key's allowlist — the proxy is the address Binance sees.
The platform signs requests from just behind Binance's clock and re-measures the offset every five minutes. It also retries the whole connection with a fresh sync when Binance reports a timestamp error. Seeing this after those retries means the server clock is drifting faster than the measurement can absorb.
Fix the host clock — install and enable NTP — rather than adjusting anything in the platform.
This is the silent fallback. When the authenticated market load fails for a reason that is not a rate limit and not a clock problem, the platform builds a second, credential-less connection so public data keeps flowing.
Charts and tickers carry on working. Currency import, deposit addresses and the balance screen do not, because all three need a signed request. Check the backend log for the warning naming the fallback, then fix the underlying credential or network problem and restart.
Markets and data
Markets are created switched off by the import. Enable the pairs you want on
/admin/finance/exchange/market. If the table itself is empty, the import has
not been run — see Currencies, markets and charts.
When the ticker stream receives an error from Binance that names specific symbols, the platform disables exactly those markets and continues rather than letting one bad row break the whole batch.
The reason is only in the backend log. Common causes: the pair was delisted, or it is not a spot market on Binance any more.
Check for a rate-limit ban before anything else. While exchange:ban_status is
set in Redis, every path that needs Binance returns nothing and logs a warning —
the site stays up and simply stops moving.
The chart settings screen reports the ban state and the remaining seconds. See Connection, rate limits and bans.
The catalogue came back empty, which on Binance means the connection is not authenticated — the currency endpoint is signed. The import is preview-first precisely so you see the delete count before agreeing to it.
Do not confirm. Verify credentials, fix them, restart, and re-run the preview.
Markets Binance has delisted are removed, except any that still carry open orders — deleting those would strand the balances those orders are holding. Cancel or settle them, then import again.
Order history on removed markets is never deleted, whatever the preview says about market counts.
Orders
The order route could not obtain a connection. Either no provider is enabled, or a rate-limit ban is active, or the connection is inside its thirty-minute failure cooldown.
That is deliberate. Provider names are scrubbed from customer-facing error text so your customers do not learn which exchange backs the platform from an error message. The full message is in the backend log.
Those limits come from the market's imported metadata, not from Binance in real time. If Binance changed a pair's minimum notional, your copy is stale until you re-import markets.
Deposits
The transaction hash is the only link between an incoming transfer and a customer — every customer sees the same Binance address for a given currency and network. If the customer never submitted a hash, or submitted the wrong one, there is no ledger entry to complete.
Reconcile manually against Binance's deposit history. Nothing in the platform will prompt you.
The deposit socket resolves the pending transaction by the signed-in user and the reference, so this is an authentication problem rather than a deposit problem. Have the customer sign in again and retry.
Three things to check, in order:
- The status. Only a deposit Binance reports as
okis credited. - The hash. Internal Binance transfers arrive labelled
Off-chain transfer <id>rather than as a chain hash. The matcher handles that wrapper, but the customer must paste the string as Binance shows it. - The currency. If the matched deposit is a different asset than the wallet the request was opened against, it is refused and the request is deleted.
The live poll runs every 15 seconds for 30 minutes. After that a wallet cron job re-checks pending deposits against Binance's history, so a customer who closed the browser is not abandoned — just slower.
The depositExpiration setting is enabled. With it on, a matched deposit is
rejected if it landed more than 15 minutes either side of the request being
created, or more than 45 minutes ago. Slow chains routinely breach that. Turn it
off unless you have a specific reason for it.
Binance kept a deposit fee equal to or larger than the transfer. Crediting zero is rejected by the wallet service, so the transaction is failed with an explanation rather than retried forever.
Withdrawals
Almost always the API key. Binance only permits withdrawals from keys that are restricted to specific IP addresses, so a key with the withdrawal permission but no allowlist will be refused every time.
Check, in order: the withdrawal permission is enabled; the key is IP-restricted; the allowlisted address is your server's real egress IP, or your proxy's.
Your Binance spot balance is short. The pre-flight check reads the free spot balance only — funds in Earn, in Funding, in a sub-account, or held by an open order on your own account are invisible to it.
Unlike the KuCoin path, the Binance path performs no internal transfer before withdrawing. Move funds into spot on Binance and retry.
That state means the wallet was debited and the exchange call may or may not have been made — the deliberate design for a crash between the two. A reconciler cron sweeps those rows against Binance's withdrawal history and either confirms or refunds them. It caps how many it scans per run, so a backlog clears over several runs.
The network name comes from the currency import. If Binance renamed a network, or you imported before a network was added, re-run the spot currency import.
If your Binance account restricts withdrawals to whitelisted destination addresses, arbitrary customer addresses will always be refused. That account setting is incompatible with self-service withdrawals.
When to restart, and what
| You changed | Restart |
|---|---|
APP_BINANCE_API_KEY or APP_BINANCE_API_SECRET |
backend and cron |
| The proxy URL, from the admin panel | Nothing — saving evicts the cached connection |
| The active provider, from the admin panel | Nothing |
NEXT_PUBLIC_EXCHANGE |
Rebuild the frontend |
| Market or currency rows | Nothing |
pm2 restart backend cron
pm2 logs backend --lines 200 | grep -i "EXCHANGE\|SPOT_DEPOSIT\|WITHDRAW"The cron worker is a separate process with its own connection. Restarting only the backend leaves the price job, the deposit verifier and the withdrawal reconciler on the old credentials.