Troubleshooting
The symptoms operators actually hit — a market that reads ACTIVE and prints nothing, a start that is refused, a pool that will not fund or withdraw, disagreeing volume figures and a price being driven twice — with the cause and the fix for each.
Work down this page in order. Most reports resolve at the first two checks.
Quick diagnostics
- Admin → System → Extensions —
ai_market_makerenabled, andecosystemenabled - Admin → System → Cron — seven jobs under
ai_market_maker, none failed - Dashboard masthead — an engine process is named, and leadership is arbitrated by Redis or the database
- Dashboard quoting meter — the market says Quoting, not Not quoting with a gate named
- Market detail → Overview — the last known price is changing
- Server log, filtered to
AI_MM— the engine states its own refusals there
The market says ACTIVE and nothing is happening
This is the single most common report, and the status column is not the answer. The engine applies four hard gates and a market failing one keeps its ACTIVE badge.
Fewer than two ACTIVE bots
A print needs two sides. Open the market's Bots tab and count the ACTIVE ones. The dashboard names this as "needs active bots" and links to the market.
Log line: Need at least 2 active bots for SYMBOL, have: 1
Note that a bot can only be activated while its market maker is ACTIVE — if you stopped the market and paused bots individually, activate the market first.
The pool is empty and real liquidity is on
With realLiquidityPercent above 0 and a total value locked of zero, there is
nothing to back an order. Either fund the pool or set real liquidity to 0.
Log line: Real liquidity enabled but no pool for SYMBOL
The daily volume budget is spent
currentDailyVolume has reached maxDailyVolume. The market stops quoting until
the UTC daily reset. Raise the budget, or use Reset daily counters on the
Configuration tab to hand out a fresh allowance now — understanding that the
budget existed for a reason.
Log line: Daily volume limit reached for SYMBOL: x/y
Volatility is above the threshold
This gate is not visible on any screen. It is evaluated from price history held in the engine's memory, which no query can reach, so the dashboard will still report the market as quoting.
Log line, throttled to once a minute:
High volatility (x.xx%), skipping SYMBOL
Either raise volatilityThreshold on the market, lower baseVolatility, or turn
pauseOnHighVolatility off.
The market is globally paused
Global Pause or Maintenance Mode on the settings screen suppress trading across every market. Prices and charts keep advancing, which is exactly what makes this one hard to spot: the chart looks alive and the tape is empty.
A market will not start
"Cannot start market maker without liquidity"
totalValueLocked is zero. Deposit to the pool first — see
Funding the pool.
"Insufficient liquidity. Minimum required: …"
aiMarketMakerMinLiquidity is checked against the pool's quote balance, not
its TVL. A pool holding plenty of base and nothing in quote fails this. Deposit
quote, or lower the setting.
It reports success and the badge stays wrong
The engine may not be running in the process that answered. The response says so: "recorded as ACTIVE, but the market-making engine is not running in this process yet." Wait one engine cycle. If it never starts, check the cron registry and the leadership block on the dashboard.
Nothing on screen, and the log says max concurrent markets reached
The setting labelled Max Concurrent Bots actually caps the number of markets the engine will drive. Raise it on the settings screen. The dashboard also raises an alert when the engine holds fewer markets than the database says are ACTIVE, which is the same fault seen from the other side.
"Cannot start market maker without bots configured"
The market maker has no bot rows at all. This should not happen — six are created in the same transaction as the market — but a partially rolled-back creation on an old build could produce it. Delete and recreate the market maker.
No ecosystem markets to select
The create wizard offers only ecosystem markets that do not already have a market
maker. A pair that already has one will not appear — the marketId column is
unique and the API refuses the duplicate with "AI Market Maker already exists for
this market".
If the list is empty entirely, you have no ecosystem markets. Create one under the Ecosystem addon first.
The pool will not fund or will not release
"Cannot withdraw from active market maker"
Pause or stop the market first. The same guard applies to the rebalance calculation, in both REPORT and EXECUTE modes.
The market is stuck ACTIVE and the buttons do nothing
An older build could leave a market the engine had lost in a state where stop reported success without changing anything and pause returned a server error — which locked the pool, because withdrawals refuse an ACTIVE market. Update. Both actions record the change properly now, and stopping also clears any synthetic depth left on the order book.
"Duplicate operation detected" on every deposit after the first
A build before v6.1.5. The wallet ledger key was derived from (pool, currency)
alone, so a pool could be funded exactly once per currency, ever — and whatever
was left after a first withdrawal could never be taken out. Update; no data is
lost.
"Wallet not found" or an opaque chain error when funding
Funding is an internal transfer between two balances the platform already holds. An older build routed it through the deposit-address machinery, so an admin holding a good balance in a token whose chain was not configured on that server could never fund the market. Update.
The price is wrong or is moving twice as far
Symptoms that all have this one cause: the price moves roughly twice as far as configured; stopping a market from the admin panel leaves it publishing; synthetic order book levels reappear after being cleared; the 1-minute candle disagrees with itself.
The engine must tick exactly once per deployment. Check the dashboard's engine
block: if leadership is arbitrated by "none", neither Redis nor the database
lease row could be reached and every process that starts the engine believes it
leads. Restore Redis, or the ai_market_maker_engine_lease table, and restart.
Cluster mode, a rolling restart whose old worker has not exited, and two development shells all produce this.
Other price symptoms:
- A step change on every chart after a restart — a build before v6.1.5, which kept no price state and began each start on a fresh path, seeded around the configured target rather than the live price. The engine now checkpoints every two minutes and resumes.
- "No price yet" on a market you just started — expected. The first checkpoint is a couple of minutes out.
- The price leaves its band — the containment force engages only in the outer 20% of the range. If the market sits at the edge, widen the range; the dashboard reports at range edge as its own state so you can act before it leaves.
The market will not follow the exchange it is set to track
The price mode reads Follow external, the reference moves, and the market does not — or moves a fraction of the distance and stops. Three separate causes, in the order worth checking.
- It has not converged yet. The band travels with the reference from v6.2.4, so a reference the price cannot reach is no longer possible — but the tether is a drift, not a jump. It closes half of any remaining gap in about 17 hours at 100% correlation and 33 at the default 50, so a market that has just been pointed at a distant reference will read converging for the best part of a day. Raise correlation strength to converge faster. On a build before 6.2.4 this was instead permanent: the containment leash out-pulled the tether two to one and the price parked at the band edge for good.
- No reference price is arriving at all. The tether needs an enabled, credentialled exchange provider, and a symbol that provider actually lists. Every failure path returns null, so before this release a dead tether looked identical to a working one. The same panel now reads Not receiving a price when nothing comes back, and the price-mode endpoint refuses a symbol the provider does not list. See Following an external price.
- It is tracking, just slowly. The tether is a restoring drift with a timescale of about one day at full strength, so half the gap closes in roughly 17 hours at 100% and 33 hours at the default 50. It will never match an intraday move tick for tick, and it is not a peg. The half-life is printed beside the correlation slider.
Even perfectly configured, this is a synthetic market that drifts toward a reference — every candle is written by the price engine from its own trades. If you need a real exchange's actual data and chart, list the pair as a provider-backed spot market instead; an AI market maker cannot be attached to one. See What users see.
Figures that disagree
Volume reads near zero shortly after midnight
currentDailyVolume is zeroed at the UTC daily reset. It is volume today, not
a rolling day. This is normal and the dashboard labels it accordingly.
The dashboard and the analytics screen report different volume
By design. The dashboard sums every market whatever its status; the analytics screen still sums ACTIVE markets only. Pausing a market part-way through a day removes it from the analytics figure along with the volume it had already traded.
P&L is zero on a busy market
A trade between two of the platform's own bots has no profit or loss — both sides are the house. On a market at 0% real liquidity, realised P&L is legitimately zero however busy the tape looks.
The all-time figure dwarfs daily, weekly and monthly
Different ledgers. All-time comes from each bot's lifetime accumulator; the period figures come from a per-fill ledger that had no writer until v6.1.0, and the individual fills before that were never captured. The screen states when the ledger begins.
Daily summaries are all zeros
Every summary written before v6.1.5 recorded zero volume, zero buys and zero sells, for the server's local day rather than the UTC day. Summary history cannot be deleted, so those rows remain. Everything written since is correct.
A bot shows a confident win rate and has never met a real user
A build before v6.1.5, which reported 50% on the list and 65% on the detail screen with the counts derived from the rate. None of it was measured. Update; those screens now show blanks where there is nothing to measure.
Orders pile up in the Ecosystem book
The backend loads open orders at startup. A build before v6.1.5 never cancelled its own quotes — one live market reached 704,353 open orders, which was enough to make the site serve connection refused while the log filled with successful startup messages.
Update, then clean up. Orders already resting do not disappear on update:
pnpm eco:mm:orders # report what is there
pnpm eco:mm:orders:clean # cancel themThe cleanup reads each order's true owner from the ecosystem row itself, which is why it can cancel rows the engine cannot.
If a current build warns that a market is at its resting-order ceiling:
N real liquidity orders are already resting, at the ceiling of 500.
That is the ceiling working. It means quotes are not being cancelled — check the expiry sweep in the log before raising the ceiling, because raising it is how a real leak gets hidden rather than fixed.
If the depth genuinely is intended, the market's own maxRestingRealOrders is set
through the admin API (PUT /api/admin/ai/market-maker/market/{id}, 50–10000, or
null for the default); AI_MM_MAX_RESTING_REAL_ORDERS moves every market at
once. Before v6.2.4 the per-market value was read by the engine but never loaded
from the database, so setting it changed nothing and only the environment
variable had any effect.
A following market has no real depth, but the book looks full
From v6.2.4 a market following an exchange keeps a floor of three real quotes a side, because its quotes expire after five minutes rather than an hour and real depth is otherwise only a by-product of the maker's own trades. The displayed ladder is republished every ten seconds either way, so an empty real book and a healthy one look identical on the chart and in the depth widget. The difference only shows when a customer's order fails to fill against depth they can see.
Two log lines explain a following market that is not topping up. Both are the engine refusing on purpose, and each is written once rather than repeatedly:
not topping up real depth while the honest spread for a 5-minute quote against BTC/USDT is 4.10%, above the cap
The reference is moving faster than a five-minute quote can be priced for, so
every quote written now would be below its value. Self-correcting — depth returns
when the reference settles. Constant means the market is tracking an asset too
volatile for the real liquidity set on it; lower realLiquidityPercent.
real depth is below its floor and the pool cannot back a quote on either side
The pool is empty, or holds only one of the two currencies — a pool with no quote
currency cannot bid, and one with no base cannot offer. Fund it, or set
realLiquidityPercent to 0 if the market is meant to be bot-to-bot.
Neither line appears on an autonomous market, which keeps the one-hour quote life and is not topped up at all. Neither appears on a futures market either: a futures quote reserves its margin when it is placed, so its depth was never unreserved in the first place.
Real users' orders vanish from the book
Fixed in v6.1.5. Every AI trade used to rebuild the market's aggregated order book by deleting every level for that symbol and writing its own back — taking real users' resting limit orders, and copy-trading followers', with them. They reappeared minutes later when a repair pass ran. While a level was missing it could not be matched against, and anything pricing off that book read the same gap.
The menu entry is missing
The extension is disabled
Admin → System → Extensions. With the row off, no menu entry renders and no cron registers.
Your role lost the permission in the key rename
The addon's keys were renamed from a dotted spelling (ai.market.maker) to an
underscored one (ai.market_maker), and the migration deletes the old rows.
Grants carry over for most of the family, but access.ai.market_maker.market —
the Markets list screen — receives grants from a retired ai.trading.market
family rather than from the addon root. Re-grant the underscored key.
Super Admin bypasses the gate outright, so an owner testing their own admin account sees nothing wrong.
Cron jobs are missing or doing nothing
No ai_market_maker category at all
The extension row is off. The scheduler reads the enabled-extension set from the database and registers addon jobs from it.
A banner says the market maker cannot make markets
The ecosystem extension is disabled, so no process in the deployment boots an
ecosystem matcher. The market maker trades ecosystem markets, so there is nothing
to quote — this is not about which process cron runs on. The other six jobs still
run and are unaffected.
The engine job reports as a no-op on a dedicated cron process
Correct behaviour under CRON_MODE=only. The engine's bots enqueue into the
ecosystem matcher, so the job runs where the matcher does — the web process
drives the same handler from its own supervisor tick.
Two background jobs report "completed" while doing nothing
A build before v6.1.5. The pool rebalancer and the external price sync both required the global trading switch to have been explicitly saved once, and it is not set on a fresh install. Every other job already treated an unset switch as on. Both now do the same.
Configuration changes have no effect
Before v6.1.5, changing a market's volatility, bias, price mode, target price, price range or forced phase — or any of a bot's settings, or its status — showed a success message, updated the database and left the running market behaving exactly as before until the next full reload, which in practice meant up to 24 hours.
A second defect sat behind it: the engine replaced its configuration wholesale on refresh while the parts that place orders and execute trades still read the copy handed to them at startup. That is why Max Daily Volume and the per-bot trade limits stopped being enforced once a market's configuration had been touched at all.
If bot edits in particular are not sticking, see the warning on Bots — the old form posted field names the server does not read, and nine of the ten risk-tolerance slider positions were refused outright, taking the rest of the request with them.
Every emergency stop in history says the same thing
The dashboard's button posted an empty request to an endpoint that has always accepted a reason and has always written it onto each market's history entry. So every such entry already recorded reads "Emergency stop triggered by admin". Those rows cannot be reconstructed. From v6.1.6 the dashboard requires a written reason; the panel on the settings screen still does not.
Collecting information for support
Have these ready:
- The addon version, from Admin → System → Extensions.
- Whether
ecosystemis enabled, and whether Scylla is reachable. - The dashboard's engine block — process, leadership and what arbitrated it.
- The affected market's quoting state and, if it is not quoting, the named gate.
- Server log lines tagged
AI_MM,AI_MM_WSorAI_RISK_MONITORaround the time of the problem. - The market's recent history entries, which name every configuration change and who made it.