Skip to content

AsyncThrottler missing pair-specific rate limits for dynamically registered trading pairs (AttributeError: 'NoneType' object has no attribute 'weight') #207

Description

@fengtality

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions