Troubleshooting
The staking failures that look like nothing is wrong — silent withdrawal queues, rewards that never accrue, fees that vanish, and capacity you cannot change.
Most staking problems are silent. Nothing errors, no screen turns red, and the first signal is a support ticket. This page is ordered by how often that happens.
Backend log modules to filter on: STAKING for the user endpoints and the
cron, ADMIN_STAKE for the admin routes.
First checks
Before anything specific, confirm these four:
- The Staking extension row is active at
/admin/system/extension - Process Staking Logs appears at
/admin/system/cronand has a recent run - A Super Admin user exists, and it is not the account you are testing with
- The Overview screen at
/admin/stakingloads with real figures rather than the "figures unavailable" state
If the Overview says figures are unavailable, the aggregation query failed. That
is deliberately not a 500 — the page keeps its header and Retry button — but the
underlying error is in the log under ADMIN_STAKE.
Symptoms
The most common one, and it is a configuration trap.
Almost always this is approval doing exactly what it is configured to do.
stakingRequireWithdrawalApproval defaults to on, so an exit requested
before endDate becomes a PENDING_WITHDRAWAL position and waits for you.
Users see the request accepted and then apparently nothing happen.
Confirm it on the Overview screen: the awaiting your decision tile will have
a non-zero count and an oldest-request age. Then open
/admin/staking/settings → Earnings and decide: turn approval off so early
exits settle immediately, or leave it on and set an auto-approve threshold so
the small ones release themselves. Either way, work the queue that has built up.
Before August 2026 this had a second cause worth knowing if you are reading an older install. The screen used to draw this switch as off while the endpoint treated an absent key as on, and because the settings form saves only the keys you change, an operator who agreed with the screen and saved wrote no row — so the platform queued exits the operator believed were settling. The screen now states what the platform actually does. If your queue predates that fix, the backlog is real and still needs working.
Note the difference between the two positions. Approval on means every early exit needs an admin. Approval off means an early exit settles immediately and pays the pool's early-withdrawal fee. A withdrawal requested after the lock has expired settles immediately either way and never enters the queue.
Work down this list:
- Is the cron running? Check
/admin/system/cron. It is hourly. Nothing accrues without it. - Is the pool
END_OF_TERMor auto-compound? Neither produces claimable rewards during the term — by design.END_OF_TERMsettles the whole reward at maturity; auto-compound folds it into the principal. - Has a full interval elapsed? A
DAILYpool credits nothing until 24 hours after the position started,WEEKLYnothing until 7 days,MONTHLYnothing until 30 days — not a calendar month. - Is
stakingAutomaticEarningsDistributionoff? With it off, matured positions still settle but nothing accrues in between until you run the accrual catch-up by hand. - Is
stakingEarningsDistributionTimeset? Only the hour matters. Periodic accrual runs only on the cron pass inside that hour, server-local. Check the cron output for "Outside the configured earnings distribution hour". - Is the position very small? Rewards are rounded to the currency's precision. A tiny stake can round to zero for several periods. Nothing is lost — the accrual is a running total, so it catches up as soon as the outstanding amount clears one unit of precision.
To settle it definitively, run the catch-up with dryRun: true against the
pool. It reports exactly what each position is owed without writing anything.
Look at the settlement has stalled alert on the Overview screen. It counts
positions that matured more than 24 hours ago and are still ACTIVE.
There is exactly one cause: the cron is not running, or it is failing. Maturity settlement is never gated by the distribution settings — holding capital past an agreed term is not a configuration option, and the code goes out of its way to make sure a toggle cannot cause it.
Check the cron process on port 4001, then the STAKING log. Failed positions
are retried three times with a five-second gap; if they still fail, a
high-priority in-app notification listing the position IDs is sent to a Super
Admin.
The refusal message names the reason. In order of likelihood:
- "You don't have a USDT wallet. Please create one first." — staking does not create the source wallet. The user needs a wallet in the pool's exact symbol and wallet type.
- "Insufficient available amount to stake in this pool" — the pool's capacity is exhausted. See the capacity item below.
- "Staking pool is not active" — the pool is
INACTIVEorCOMING_SOON. - "Amount must be at least ..." / "must not exceed ..." — the pool's minimum or maximum stake.
- "Amount exceeds the allowed precision" — too many decimal places for that currency.
- A KYC refusal — the
invest_stakingfeature gate.
Also check the rate limit: five stake attempts a minute per user.
You cannot, from the admin form. The update endpoint deliberately strips
availableToStake out of the request body, because writing back the value the
form loaded would revert every stake and settlement that happened since the page
opened. The input on the edit screen is inert and nothing on screen says so.
Capacity changes only through an explicit signed delta:
curl -X PUT "https://your-host/api/admin/staking/pool/POOL_ID" \
-H "Content-Type: application/json" \
-H "Cookie: accessToken=..." \
-d '{"capacityDelta": 50000}'A negative delta is refused if the pool has less than that amount available, so you cannot drive capacity negative. Set capacity generously at creation time to avoid needing this.
Two innocent explanations before you suspect a bug:
- You are testing as the Super Admin. The fee collector skips collection entirely when the actor is the Super Admin, rather than crediting them from themselves. Test with a normal user.
- The pool's admin fee is 0. No fee, no row.
The one that is not innocent: if there is no Super Admin role, or the role has
no users, every fee is dropped and logged as
[CRITICAL] ... platform fees are being dropped. No bookkeeping row is written
in that case — deliberately, so the earnings screen never shows revenue the
platform did not receive. There is no retry and no queue. Fix the Super Admin,
and treat the fees earned in the meantime as lost.
The Overview flags a pool when its realised APR is more than 5% below its net promised APR.
First confirm you are reading the right comparison. A pool advertising 12% with a 20% admin fee is supposed to pay the staker 9.6%. The console already accounts for that — it measures against net promised, not advertised — so a flag is not just the fee showing up.
Then check:
- Has distribution been off or restricted to an hour? Accrual lag shows up as a shortfall until the next run catches up.
- Was the APR raised recently? Existing positions keep the APR they snapshotted at stake time, so the pool's realised rate lags its new advertised rate until the old positions roll off. This is correct behaviour.
- Pools that show "not measurable yet" are
END_OF_TERMor auto-compound. They never advance a watermark during the term, so there is no denominator. That is not a fault.
The one-off bonus endpoint allows one run per pool per cycle, where the cycle
length comes from the pool's earning frequency — 24 hours for DAILY, 7 days
for WEEKLY, 30 days for MONTHLY, and daily for END_OF_TERM. The window
slides from the epoch rather than resetting at midnight, so two requests either
side of midnight fall in the same bucket.
If you genuinely need a second payout in the same cycle, run it with the other
distributionType label — regular and bonus occupy separate buckets, and
both write BONUS earning rows.
If what you actually wanted was to catch up on APR that should already have
accrued, you want the other endpoint: /api/admin/staking/earnings/distribute
(plural). It has no cycle limit because it only ever credits the outstanding
delta.
- Nothing has accrued yet — see the accrual checklist above.
- Everything already claimed.
- The pool is auto-compound, in which case the message is different and explicit: rewards come back with the principal at maturity and are not separately claimable.
- The position was
CANCELLED, which forfeits unclaimed rewards permanently.
Both are guarded, and the guard applies to forced deletion too.
A pool cannot be deleted while it holds ACTIVE or PENDING_WITHDRAWAL
positions. A position cannot be deleted while it is itself ACTIVE or
PENDING_WITHDRAWAL. In both cases the principal has not been returned, and
deleting the row would strand it.
Settle first — complete, cancel, or let the term run — then delete. Note that cancelling forfeits the staker's unclaimed rewards; completing does not.
Only one ACTIVE pool is allowed per (symbol, walletType) pair. Multiple
INACTIVE or COMING_SOON tiers on the same asset are fine, so you can stage a
replacement — but switching over is two steps: deactivate the old pool, then
activate the new one.
Existing positions in the deactivated pool keep accruing and still settle normally.
Both navigation trees are gated on the extension flag. Check
/admin/system/extension and confirm the Staking Crypto row (product ID
37434481) is active, then reload.
If the toggle animates on and reverts on reload, the write failed — look for
EXTENSION in the backend log.
Deliberate. A staking book holding BTC and USDT positions has no single denomination, so no unit is printed on any rolled-up figure. Money appears only inside rows that carry exactly one symbol: the pool table, the asset table and the withdrawal queue. Book-level rollups are counts.
If your entire live book is one asset, the unit appears automatically.
Correct, and intentional. An early exit is priced from the moment the user asked to leave, not from the moment you decide. A request filed inside the lock period keeps its early-exit price no matter how long it waits.
The alternative — pricing from settlement — meant the longer an admin took the
cheaper the exit became, and once endDate passed in the queue it became free.
The queue labels these rows "term ended while waiting" so the situation is
visible.
The message reads "Final reward accrual failed for position X; continuing with principal return".
Settlement tries to credit the last outstanding reward before the position goes terminal. If that step fails, the failure is logged and the principal is returned anyway — stranding someone's capital to protect a reward calculation would be strictly worse.
The consequence is a small unpaid reward on that one position. Investigate the logged cause; you can pay the shortfall with a one-off bonus distribution.
Escalation
If none of the above fits, collect before you ask for help:
- The position or pool ID.
- The
STAKINGorADMIN_STAKElog lines around the timestamp. - The output of the accrual catch-up with
dryRun: truefor that pool. - Whether the cron has run since the problem started.
- Which of the eight staking settings actually have saved values, rather than what the screen displays.
That last one resolves more staking tickets than anything else on this page.