Summary
Trading connectors are created with trading_pairs=[], so the connector's AsyncThrottler builds its rate limits from an empty pair list at init. When a trading pair is later registered dynamically (appended to connector._trading_pairs), the throttler is never updated. Any pair-specific limit_id (e.g. Bybit's v5/position/set-leverage-{PAIR}, v5/order/create-{PAIR}) then resolves to None, and the client crashes with:
AttributeError: 'NoneType' object has no attribute 'weight'
Reported by a community user on Discord (bybit_perpetual, Ubuntu, clean install, one-server mode); root cause verified against the code.
Root cause
UnifiedConnectorService._create_trading_connector() builds connectors with trading_pairs=[] (services/unified_connector_service.py:695, same for Gateway at :687 and data connectors at :725).
- For connectors with pair-specific rate limits, e.g.
bybit_perpetual:
@property
def rate_limits_rules(self) -> List[RateLimit]:
return web_utils.build_rate_limits(self.trading_pairs)
The AsyncThrottler is constructed once from this list during connector init — with no pairs, so no pair-specific limits exist.
- Pairs added later via
connector._trading_pairs.append(...) never reach the throttler, so throttler.execute_task(limit_id=...) finds no matching rate limit for pair-scoped endpoints.
Note: reassigning connector._throttler = AsyncThrottler(new_limits) does not fix it — WebAssistantsFactory captures the original throttler reference at connector init, so the new object is never used by the REST/WS assistants.
Symptoms
POST to set position mode / leverage returns 500: 'NoneType' object has no attribute 'weight'
- Order placement via executors silently retries and fails (
Open order failed... Retrying N/10) — the order never reaches the exchange and no useful error surfaces.
Affected connectors
Any connector whose rate limits are built per trading pair (e.g. bybit_perpetual; other connectors with {PAIR}-templated limit IDs are likely affected the same way).
Suggested fix
When a trading pair is registered on a connector, sync the existing throttler in place using AsyncThrottler.add_rate_limits(connector.rate_limits_rules). This mutates the throttler object the WebAssistantsFactory already holds, and it is idempotent — safe to call unconditionally on every pair registration rather than only for unseen pairs (a pair can also land in _trading_pairs via the data-connector bootstrap path without going through the registration helper).
The reporter confirmed this fix end-to-end on two pairs after a container rebuild: BUY/SELL executed on first attempt (current_retries: 0), and position close was confirmed by the exchange.
Summary
Trading connectors are created with
trading_pairs=[], so the connector'sAsyncThrottlerbuilds its rate limits from an empty pair list at init. When a trading pair is later registered dynamically (appended toconnector._trading_pairs), the throttler is never updated. Any pair-specificlimit_id(e.g. Bybit'sv5/position/set-leverage-{PAIR},v5/order/create-{PAIR}) then resolves toNone, and the client crashes with:Reported by a community user on Discord (bybit_perpetual, Ubuntu, clean install, one-server mode); root cause verified against the code.
Root cause
UnifiedConnectorService._create_trading_connector()builds connectors withtrading_pairs=[](services/unified_connector_service.py:695, same for Gateway at:687and data connectors at:725).bybit_perpetual:AsyncThrottleris constructed once from this list during connector init — with no pairs, so no pair-specific limits exist.connector._trading_pairs.append(...)never reach the throttler, sothrottler.execute_task(limit_id=...)finds no matching rate limit for pair-scoped endpoints.Note: reassigning
connector._throttler = AsyncThrottler(new_limits)does not fix it —WebAssistantsFactorycaptures the original throttler reference at connector init, so the new object is never used by the REST/WS assistants.Symptoms
POSTto set position mode / leverage returns500: 'NoneType' object has no attribute 'weight'Open order failed... Retrying N/10) — the order never reaches the exchange and no useful error surfaces.Affected connectors
Any connector whose rate limits are built per trading pair (e.g.
bybit_perpetual; other connectors with{PAIR}-templated limit IDs are likely affected the same way).Suggested fix
When a trading pair is registered on a connector, sync the existing throttler in place using
AsyncThrottler.add_rate_limits(connector.rate_limits_rules). This mutates the throttler object theWebAssistantsFactoryalready holds, and it is idempotent — safe to call unconditionally on every pair registration rather than only for unseen pairs (a pair can also land in_trading_pairsvia the data-connector bootstrap path without going through the registration helper).The reporter confirmed this fix end-to-end on two pairs after a container rebuild: BUY/SELL executed on first attempt (
current_retries: 0), and position close was confirmed by the exchange.