Troubleshooting
Symptom-first diagnosis for the KuCoin provider — the three faces of a passphrase problem, the silent Redis ban, the 30-minute cooldown, region blocks, and money that moves but does not settle.
Start at Admin → Finance → Exchange (/admin/finance/exchange). Loading that
page runs a live credential test, so the badge you see is current rather than
remembered. Press Verify to run it again after any change.
Do not start at Admin → System → Health. Its exchange check only looks for a
non-empty APP_KUCOIN_API_KEY and APP_KUCOIN_API_SECRET. It cannot see the
passphrase, and it reports green on the most common broken configuration this
product has.
Fast triage
| Symptom | Most likely cause |
|---|---|
| Every spot route errors with "Failed to start exchange provider: kucoin" | Passphrase missing or empty, or the provider is in its 30-minute cooldown |
| Charts and tickers fine, balances and deposit addresses fail | Passphrase present but wrong — the client fell back to unauthenticated |
| Verify says "check your API key and secret" but they are correct | It is the passphrase. The message does not mention it |
| Verify says credentials are missing | APP_KUCOIN_API_KEY or _API_SECRET is unset. The passphrase is not part of that check |
| Health is green, nothing works | Health does not check the passphrase |
| Everything reports success and nothing happens | A ban marker is set in Redis |
| HTTP 451, "restricted location", "Eligibility" | Your server's region is blocked by KuCoin. Configure a proxy |
Fixed .env but behaviour unchanged |
Credentials are read once per process — restart the backend |
| The enable toggle returns 403 | lic/37179816.lic is missing |
| Withdrawals fail with "Transfer to trade account failed" | Nothing in the Main account to move, or no Transfer permission on the key |
| Charts draw the wrong symbols | NEXT_PUBLIC_EXCHANGE is still "bin" |
| Ticker batch fails: "symbols must be of the same type" | A futures or swap symbol got into exchange_market |
| Markets imported but the trade page is empty | Imports arrive disabled — enable them |
| Cannot enable a market despite full exchange permissions | The status endpoints need edit.ecosystem.market |
Diagnosis in detail
This is the first thing to rule out, every time.
Absent or empty. ccxt requires password for KuCoin and raises before any
network call. The platform's own guard checks only the key and the secret, so the
throw arrives as a generic initialisation failure. It retries three times, five
seconds apart, then gives up and returns nothing. Every spot route answers
Failed to start exchange provider: kucoin, and the whole spot stack is dark.
Present but wrong. The client is built, loadMarkets() runs, and KuCoin
rejects the signed account probe inside currency loading. The platform then
replaces the client with an unauthenticated one and logs:
Falling back to an unauthenticated kucoin instance — authenticated data
(deposit/withdraw networks, balances) will be unavailable until valid API
credentials are configured.Public data keeps flowing. Markets load, tickers update, charts look correct. Balances, deposit addresses and withdrawals do not work. This is the expensive one, because nothing on screen says anything is wrong.
Correct but stale. You changed .env and did not restart. Credentials are
read once, when the client is first built, and the built client is cached for the
life of the process.
Grep the backend log for Falling back to an unauthenticated kucoin instance to
separate the second case from the first. Then confirm all three variables are
present, spelled exactly APP_KUCOIN_API_KEY, APP_KUCOIN_API_SECRET,
APP_KUCOIN_API_PASSPHRASE, and restart.
Check for the ban marker:
redis-cli GET exchange:ban_status
redis-cli TTL exchange:ban_statusA value means a ban is active. While it is set, ExchangeManager returns nothing
and every spot path — the prices cron, order reconciliation, deposit detection,
withdrawals, the admin screens — quietly no-ops while reporting green. It is the
single most confusing state this integration has.
The marker is written when KuCoin returns a rate-limit error, or when its error text contains an "IP banned until" timestamp. It carries a TTL matching the ban, so it clears itself; any ban longer than 24 hours is clamped down to 24 hours, and a timestamp already in the past is ignored rather than stored.
Deleting the key manually unwedges the stack, but find out what tripped the limit
first. The usual causes are a candle build with rateLimit set too low, and the
KuCoin ticker path — which polls rather than streams, unlike the other providers.
Three consecutive initialisation failures put the provider into a cooldown. For
the next 30 minutes it returns nothing without attempting a connection, so a
.env fix appears to have no effect at all.
Restarting the backend clears it, because the counter lives on the in-process manager. Waiting also works, but there is no reason to wait.
Symptom to recognise: the log shows three Failed to initialize exchange entries
five seconds apart, then goes quiet while requests keep failing.
KuCoin does not serve every region. The Exchange screen's Regions tab lists the ones it names: the United States, North Korea, Singapore, Hong Kong, Iran and the Crimean region. A server in one of them gets 451 on every call, public ones included, and verification reports:
Access denied: Your server's location is blocked by this exchange. Please configure a proxy in the Settings tab to connect through an allowed region.
Configure a proxy on /admin/finance/exchange → Settings. Schemes are
http://, https://, socks4:// and socks5://, with optional user:pass@.
Test before saving — the test opens a public connection through the proxy and
calls fetchTime. It distinguishes three failures for you: an unreachable proxy,
a proxy whose own exit is also blocked, and proxy authentication failure (407).
After saving, whitelist the proxy's exit address on KuCoin, not your app server's. Saving a proxy evicts the cached client, so the next call goes out from the new address immediately.
With no proxy set, the platform connects over an agent pinned to IPv4 — that is the address to whitelist in the normal case.
Signed requests carry a timestamp measured against KuCoin's own clock. The
platform brackets a fetchTime round trip so network latency is not mistaken for
drift, signs 500 ms behind the server on purpose, and refreshes the offset in the
background every five minutes.
If verification reports "Server time synchronization failed", fetchTime failed
twice — which is usually a network or proxy problem, not a clock problem. If you
see timestamp rejections instead, check the server clock:
timedatectl statusA drift beyond five seconds is logged as a warning at sync time. Enable NTP rather than tuning anything in the platform.
Work down the list.
Is the deposit currency the same as the wallet currency? A mismatch deletes the transaction and returns "Invalid deposit currency" — the platform will not credit BTC into a USDT wallet.
Is depositExpiration enabled? If so, a deposit whose exchange timestamp sits
more than 15 minutes either side of the request, or is more than 45 minutes old,
is marked TIMEOUT instead of credited. A deposit for which KuCoin reports no
timestamp is never expired.
Was the network right? KuCoin names chains by token standard. If the user sent on a chain the platform mapped differently, the deposit exists at KuCoin but the lookup does not find it. For KuCoin the live path already tries three ways — with the mapped chain, without a chain parameter, and by fetching everything and filtering — so a persistent miss usually means the deposit is not there at all.
Is the catch-up job running? processSpotPendingDeposits runs every 15 minutes
and handles anything the 30-minute live window missed. Check the cron screen.
Note that the main → trade transfer runs after the credit and is
best-effort. A failure there is logged as KuCoin main->trade transfer failed for deposit <id> and does not affect the customer's balance.
"Transfer to trade account failed". The main → trade transfer did not
return an id. Either the Main account has no balance in that asset, or the API
key lacks Transfer permission. This aborts before KuCoin is asked to send
anything, so nothing left the exchange.
"Withdrawal request failed. Please try again or contact support." This is
fixed text covering every exchange-side failure. The real error is in the backend
log under WITHDRAW and in the admin action log for that request.
Stuck at PROCESSING with no reference id. That combination means the user was
debited but the send was never issued — usually a restart mid-flight. The
reconcileSpotWithdrawals job runs every 5 minutes specifically to resolve
these, confirming with KuCoin if the send actually happened and refunding if not.
Give it a cycle before intervening by hand.
The balance pre-check did not stop it. By design it cannot. The balance read and the insufficient-funds comparison share one error handler, so a shortfall is warned about and the withdrawal proceeds to KuCoin, which rejects it. Do not rely on it as a guard.
To trace a withdrawal at KuCoin, search its withdrawal history for the client
order id wd_<transaction id> used by the admin approval path.
A contract symbol has reached exchange_market. ccxt loads KuCoin's spot, swap
and futures markets together, and a single BTC/USDT:USDT row makes every
multi-symbol call fail, taking the whole ticker batch with it — not just that
pair.
The importer filters these out, and a runtime filter drops non-spot symbols before batch calls. If one is still present, it predates the filter. Find any market whose symbol contains a colon and delete it, then re-import.
KuCoin accepts depths of 5, 20, 50 or 100 only. The backend snaps any other request to the nearest allowed value and trims the response back before sending it on, so the display is correct even when the request was not.
If depth still looks off, check NEXT_PUBLIC_EXCHANGE. Set to "kuc" the
browser uses KuCoin's ladder; left at "bin" it asks for Binance depths that
KuCoin will not serve, and the TradingView chart is drawn against Binance symbols
as well. Changing it requires a frontend rebuild, not just a restart.
That is deliberate. The order placement path and the deposit-address path replace
kucoin, binance and okx in error text with *** before it leaves the
backend, so customers never learn which exchange backs the platform.
The unscrubbed message is in the backend log. A user screenshot will never be enough to diagnose one of these.
What to collect before opening a support ticket
- The verification message from
/admin/finance/exchange, verbatim. - Whether
APP_KUCOIN_API_PASSPHRASEis set — its presence, never its value. redis-cli GET exchange:ban_statusand its TTL.- Backend log lines tagged
EXCHANGEfrom the last restart onward, especially anyFalling back to an unauthenticated kucoin instance. - Whether a proxy is configured, and which address is whitelisted on KuCoin.
- For a money issue: the transaction id, its status, and its
referenceId.