The background orderbook refresh thread in OrderBookManager._thread_refresh_order_book crashes permanently if _run_get_orders() returns None (e.g. API timeout or 429).
Root cause
_run_get_orders() catches exceptions and returns None on failure (line 333). The state update correctly guards with if orders is not None (line 377). But the debug log immediately after does not:
# orderbook.py, line 388-392
self.logger.debug(
f"Fetched the order book"
f" (orders: {[order.id for order in orders]}, " # TypeError if orders is None
...
)
This throws TypeError: 'NoneType' object is not iterable. The except block at line 393 only catches ValueError, so the TypeError propagates and kills the thread.
Impact
After a single transient API failure:
- The daemon refresh thread dies silently (no log, no restart)
_state is never updated again
- The bot continues operating with a stale orderbook indefinitely
- Stale prices/positions lead to incorrect order placement
This is particularly easy to trigger with the rate limiting described in #78.
Reproduction
- Start the market maker
- Temporarily block or rate-limit the CLOB API (e.g. firewall rule, or wait for a 429)
_run_get_orders() returns None
- Debug log crashes the thread
- No further orderbook refreshes happen
Fix
Either guard the log:
if orders is not None:
self.logger.debug(
f"Fetched the order book"
f" (orders: {[order.id for order in orders]}, "
...
)
Or broaden the exception handler from except ValueError to except Exception so transient failures don't kill the thread.
The background orderbook refresh thread in
OrderBookManager._thread_refresh_order_bookcrashes permanently if_run_get_orders()returnsNone(e.g. API timeout or 429).Root cause
_run_get_orders()catches exceptions and returnsNoneon failure (line 333). The state update correctly guards withif orders is not None(line 377). But the debug log immediately after does not:This throws
TypeError: 'NoneType' object is not iterable. The except block at line 393 only catchesValueError, so theTypeErrorpropagates and kills the thread.Impact
After a single transient API failure:
_stateis never updated againThis is particularly easy to trigger with the rate limiting described in #78.
Reproduction
_run_get_orders()returnsNoneFix
Either guard the log:
Or broaden the exception handler from
except ValueErrortoexcept Exceptionso transient failures don't kill the thread.