Troubleshooting
Symptom-first diagnosis for the XT provider — credentials that verify but do not work, deposits that never credit, the ban switch that silences everything while reporting green, and orders that fill on XT but not in the platform.
Start at Admin → Finance → Exchange and press Verify Credentials. It is the
only check in the product that exercises the full authenticated path — clock
sync, loadMarkets(), then fetchBalance() — and its failure messages are
specific enough to be diagnostic on their own.
If that passes and things are still wrong, the answer is almost always one of the two failure modes that report green: the public-instance fallback, or the ban switch. Both are covered below.
Fast triage
| Symptom | Most likely cause |
|---|---|
| Admin → Finance → Exchange says No active exchange provider | No exchange row has status = true |
Toggling XT on returns 403 with licenseRequired |
lic/54510301.lic is missing — activate the licence |
| API credentials are missing from environment variables | .env not set, or the backend has not restarted since it was |
| Invalid API credentials on a key you know is right | The server's source IP is not on XT's whitelist, or it presented IPv6 |
| Access denied: Your server's location is blocked | HTTP 451 — XT geo-blocks the server. Configure a proxy |
| Charts and tickers fine, but no deposit addresses and no balances | The connection fell back to a public, unauthenticated instance |
| Everything is quiet — no prices, no fills, no deposits — and nothing errors | The ban switch is set in Redis |
| Orders fill on XT but never update in the platform | processPendingSpotOrders has stopped |
| A deposit arrived on-chain and was never credited | The network name is unmapped for verification |
Withdrawal marked COMPLETED but XT still shows it pending |
The optimistic default when fetchWithdrawals cannot find it |
| A market cannot be enabled despite full exchange permissions | The status endpoint needs edit.ecosystem.market |
| Portfolio values frozen | processCurrenciesPrices has stopped, or the provider is inactive |
| Every spot endpoint answers 503 | No active provider, or startExchange() is returning null |
| Trade page crashes on a specific market | A non-numeric field reached the browser — check the ticker stream for that symbol |
Diagnosis in detail
This is the public-instance fallback, and it is the single most misleading state this product can be in.
When loadMarkets() fails for a reason that is neither a rate limit nor a
timestamp error, the manager does not stop. It closes the authenticated instance,
builds an unauthenticated one, loads markets on it, and caches that. Public
data — tickers, candles, order book, market list — keeps flowing. Everything
authenticated silently returns nothing.
The signature in the backend log is exact:
Falling back to an unauthenticated xt instance — authenticated data
(deposit/withdraw networks, balances) will be unavailable until valid
API credentials are configured.Symptoms: deposit screens show no networks for any asset, the exchange balance page is empty, withdrawals fail, and the currency import writes far fewer rows than you expect. The front page looks perfect.
Fix the underlying credential or network problem, then restart the backend — the fallback instance is cached and nothing evicts it.
The ban switch. When XT rate-limits the platform, the unblock time is written to
the Redis key exchange:ban_status. While that key is set,
ExchangeManager.startExchange() returns null before doing anything, so every
spot path — prices, order reconciliation, deposit verification, withdrawals, HTTP
routes — returns without acting. No route 500s. No admin screen shows it.
The only signal is a warning in the backend log:
Exchange is banned for 41 minutes and 12 seconds moreThe key carries a TTL and self-clears; the ban is clamped to a maximum of 24 hours no matter what XT's error text claims, because that value is scraped out of free-form text and a stray digit run would otherwise take the platform down for years.
To clear it early, delete the key — but only once XT has actually stopped throttling, or you will simply be re-banned:
redis-cli DEL exchange:ban_statusThe usual cause is a chart build with too small a request delay. Raise the delay at Admin → Finance → Exchange → Chart before rebuilding large ranges.
Almost always the source address rather than the key.
# What XT actually sees, over IPv4
curl -4 https://api.ipify.orgCompare that with the whitelist on the XT key. Three things commonly break it:
- A NAT gateway or egress proxy in front of the server, so the address is not the one you see on the box.
- A proxy configured on the provider row. A proxy replaces the platform's forced-IPv4 agent entirely, so the address to whitelist is the proxy's — and it must be reachable over IPv4.
- A server migration or floating-IP reassignment since the key was created.
XT's key whitelist does not accept IPv6, which is why the platform pins exchange
traffic to family: 4 and names XT in the comment. If you have overridden that
with a proxy, you have also given up that protection.
Check the network name first. Deposit verification maps the platform's chain name
to XT's long-form network name — TRC20 becomes Tron, ERC20 becomes
Ethereum, BEP20 becomes BNB Smart Chain — and returns null for any
chain not in its table. On null, the deposit query goes to XT without a chain
filter, and the match can fail.
The table covers Tron, Ethereum, BNB Smart Chain, Polygon, Arbitrum, Optimism, Avalanche C-Chain, Solana, Bitcoin, Litecoin, Dogecoin, Base, Ethereum Classic and Bitcoin Cash, under several aliases each. Anything else is unmapped.
The coins are not lost — they are in your XT account. Confirm the deposit in XT's own interface, then credit the wallet manually through the admin wallet tools.
Also check the fee: if XT's network fee is greater than or equal to the deposit, the platform deliberately credits nothing and tells the customer the deposit does not cover the fee.
After calling withdraw, the platform re-reads the withdrawal from
fetchWithdrawals to pick up the true fee and status. If it cannot find the row,
it records the expected fee and marks the withdrawal COMPLETED.
That default is optimistic. A withdrawal XT has not yet exposed over the API, or one still under review, can be marked complete in the platform. A subsequent failure on XT's side is not reflected back.
Reconcile spot withdrawals against XT's own withdrawal history rather than trusting the platform status. If you process meaningful volume, do it on a schedule.
processPendingSpotOrders runs every 60 seconds and is the only thing that
settles an open order. Check Admin → System → Cron for its last run and last
error.
If it is running but doing nothing, the provider is the problem, not the job — it returns immediately when there is no active provider or the ban switch is set.
While an order is unsettled the customer's funds stay held in inOrder, so this
is a support-visible failure within minutes.
They will not, and that is correct. For a BUY, XT reports info.executedQty as
the amount spent, not the amount bought. The platform divides it by
info.avgPrice before crediting the wallet.
Your platform records the base amount the customer received. XT's export records the quote amount they spent. Multiply by the average price to compare them.
XT returns some ticker fields as raw strings — notably 24-hour base volume, which
arrives in info.q rather than baseVolume. The ticker stream coerces every
numeric field to a number for XT specifically, because a string reaching the
browser makes .toFixed() throw and takes the whole page with it.
If you see this on a single symbol, capture the raw ticker payload for it: a field the coercion does not cover is the likely cause, and it belongs in a support report.
They are wrong. SPOT wallet rows recorded while a different exchange held the coins are claims against an XT account that never received them. Currencies, markets, symbols, precisions and network names all differ between providers.
There is no migration path in the product. Switching provider on an install with customer balances requires settling those balances first.
Networks are never stored in the database. The deposit and withdrawal screens call XT live on every load, and that call is authenticated. An empty list means the authenticated path is broken — the public-instance fallback, a key without deposit-address permission, or an active ban.
The endpoints that enable and disable a market are gated on
edit.ecosystem.market, not edit.exchange.market, despite living under
/api/admin/finance/exchange/market. Grant it to the role. It is required
whether or not you run the Ecosystem addon.
Log lines worth grepping for
All of these come from the EXCHANGE module in the backend log.
| Line | Means |
|---|---|
API credentials for xt are missing. |
.env is empty or unread; three of these locks retries for 30 minutes |
API credentials for xt are invalid. |
XT rejected the key; the connection is being rebuilt without credentials |
Falling back to an unauthenticated xt instance |
The public-instance fallback is active |
Exchange is banned for … more |
The ban switch is set |
Significant time offset with server: …ms |
Clock drift over 5 seconds — fix NTP on the server |
Timestamp error for xt, recreating exchange with fresh time sync |
Expected and self-healing; only worrying if it repeats constantly |
Using proxy for xt |
A proxy is configured on the provider row |
Failed to load markets: … |
The reason the fallback is about to happen |
What to include in a support report
- The exact message from Verify Credentials.
- Whether a proxy is configured, and the result of Test Proxy.
- The backend log around the failure, including any of the lines above.
- Whether
exchange:ban_statusis set:redis-cli TTL exchange:ban_status. - The provider row state — active, licensed, version — from Admin → Finance → Exchange.
- For a money problem: the currency, the network as the customer saw it, the transaction id, and what XT's own interface shows for it.