Summary
Sibling of #207, same root cause, different pair-derived state left stale. Trading connectors are created with trading_pairs=[], so connectors that build trading rules per pair (XRPL queries tick size / transfer rate / AMM info on-ledger for each pair in self._trading_pairs) initialize with an empty _trading_rules dict. When a pair is later registered dynamically, add_market() initializes the order book but never adds the pair to connector._trading_pairs nor re-runs _update_trading_rules(). Any executor created for that pair then dies at startup:
2026-08-07 05:33:24,767 - hummingbot.core.utils.async_utils - ERROR - Unhandled error in background task: 'USDC-XRP'
Traceback (most recent call last):
File ".../hummingbot/core/utils/async_utils.py", line 9, in safe_wrapper
return await c
File ".../hummingbot/strategy_v2/executors/executor_base.py", line 194, in control_loop
await self.on_start()
File ".../hummingbot/strategy_v2/executors/executor_base.py", line 182, in on_start
await self.validate_sufficient_balance()
File ".../hummingbot/strategy_v2/executors/order_executor/order_executor.py", line 388, in validate_sufficient_balance
price_for_validation = self.get_price_for_balance_validation()
...
trading_rule = self._trading_rules[trading_pair]
KeyError: 'USDC-XRP'
Symptoms
Root cause
UnifiedConnectorService._create_trading_connector() builds connectors with trading_pairs=[] (services/unified_connector_service.py:695).
- XRPL's
_update_trading_rules() iterates only self._trading_pairs (hummingbot/connector/exchange/xrpl/xrpl_exchange.py, _make_trading_rules_request_impl), so no rules are ever built.
- The dynamic registration path —
ExecutorService._prepare_market() → TradingService.add_market() → order-book init in UnifiedConnectorService — touches only the order book tracker. Neither connector._trading_pairs nor trading rules are updated. The base ExchangePyBase.add_trading_pair() likewise only subscribes the order book.
- The 30-minute
_trading_rules_polling_loop cannot self-heal it either, since it re-reads the same empty _trading_pairs.
Most CEX connectors dodge this because one exchange-info request returns rules for all pairs regardless of _trading_pairs. Any connector building rules from self._trading_pairs (XRPL today; others with per-pair rule construction) is affected.
Relationship to #207
Same defect, different victim: #207 is the AsyncThrottler never learning pair-templated rate limits; this is trading rules never being built. Both stem from dynamic pair registration not re-syncing pair-derived connector state, and #207's suggested add_rate_limits() sync does not fix this one (nor vice versa). Suggests a single registration helper that syncs all pair-derived state — _trading_pairs, throttler limits, trading rules — at every registration site, including the data-connector bootstrap path #207 already flags as bypassing registration helpers.
PR to follow addressing both.
Summary
Sibling of #207, same root cause, different pair-derived state left stale. Trading connectors are created with
trading_pairs=[], so connectors that build trading rules per pair (XRPL queries tick size / transfer rate / AMM info on-ledger for each pair inself._trading_pairs) initialize with an empty_trading_rulesdict. When a pair is later registered dynamically,add_market()initializes the order book but never adds the pair toconnector._trading_pairsnor re-runs_update_trading_rules(). Any executor created for that pair then dies at startup:Symptoms
POST /executors/returns 201 Created, the executor registers asRUNNING, but its control loop is already dead:order_id: None,current_retries: 0, no order ever reaches the ledger. From the API consumer's perspective the order was placed; on-chain nothing happened. This zombie state is arguably worse than AsyncThrottler missing pair-specific rate limits for dynamically registered trading pairs (AttributeError: 'NoneType' object has no attribute 'weight') #207's visible 500.xrpl/USDC-XRPandRLUSD-XRP(any XRPL pair): 3/3 executor creates crashed identically (2026-08-07, Dockerhummingbot/hummingbot-api:latest).Root cause
UnifiedConnectorService._create_trading_connector()builds connectors withtrading_pairs=[](services/unified_connector_service.py:695)._update_trading_rules()iterates onlyself._trading_pairs(hummingbot/connector/exchange/xrpl/xrpl_exchange.py,_make_trading_rules_request_impl), so no rules are ever built.ExecutorService._prepare_market()→TradingService.add_market()→ order-book init inUnifiedConnectorService— touches only the order book tracker. Neitherconnector._trading_pairsnor trading rules are updated. The baseExchangePyBase.add_trading_pair()likewise only subscribes the order book._trading_rules_polling_loopcannot self-heal it either, since it re-reads the same empty_trading_pairs.Most CEX connectors dodge this because one exchange-info request returns rules for all pairs regardless of
_trading_pairs. Any connector building rules fromself._trading_pairs(XRPL today; others with per-pair rule construction) is affected.Relationship to #207
Same defect, different victim: #207 is the AsyncThrottler never learning pair-templated rate limits; this is trading rules never being built. Both stem from dynamic pair registration not re-syncing pair-derived connector state, and #207's suggested
add_rate_limits()sync does not fix this one (nor vice versa). Suggests a single registration helper that syncs all pair-derived state —_trading_pairs, throttler limits, trading rules — at every registration site, including the data-connector bootstrap path #207 already flags as bypassing registration helpers.PR to follow addressing both.