The Real-Time Bus in Odoo 19: Longpolling Is Gone

Odoo's real-time layer has quietly finished a years-long migration from HTTP longpolling to WebSocket. If your custom code still talks to the bus the old way, Odoo 19 is the version where it stops answering.

The bus is the part of Odoo most engineers never think about until it breaks: the channel that pushes live notifications (new messages, presence, UI refreshes) from server to browser without a page reload. In Odoo 19 it finished a transition that had been underway for several versions, and the cleanup removed things custom code may have been relying on.

The legacy presence endpoint is gone

The /websocket/update_bus_presence route no longer exists. Code that posted to it to announce user presence, usually older custom widgets or integrations, will get a 404. Presence is now handled inside the WebSocket subscription flow, not as a separate HTTP call.

Route types changed: json became jsonrpc

The bus controller's JSON routes (/websocket/peek_notifications, /websocket/on_closed) moved from type='json' to type='jsonrpc'. If you wrote a controller that _inherits one of these or registered a route alongside them with the old type, the declarations no longer match. This is a small change that produces a confusing failure, because the route still exists. It just no longer behaves the way an inheriting override expects.

Presence overrides need rewriting

v18's ir.websocket exposed a cluster of presence methods: _get_missed_presences_identity_domains(), _get_missed_presences_bus_target(), and _build_presence_channel_list(). v19 collapses all of that behind a single hook, _after_subscribe_data(data), called once after a subscription is established. Custom code that overrode the old granular methods to inject extra channels or presence logic has nothing to attach to anymore; that logic moves into the new hook.

Channel subscription now includes implied groups

Group-based bus channels are built from a different field. v18 used self.env.user.groups_id; v19 uses self.env.user.all_group_ids, which includes implied groups. If your module pushes notifications to a group channel, v19 will deliver them to more users than v18 did: users who hold the group by implication rather than direct assignment. Usually that is correct. Occasionally it is a surprise. Either way, know it changed.

The reliability angle: PoolError and the connection budget

v19's bus module added an import that tells you where Odoo's own thinking went: from psycopg2.pool import PoolError. The WebSocket architecture holds long-lived connections, and each one competes for the same PostgreSQL connection pool as everything else: interactive requests, scheduled jobs, report generation.

This is the failure mode to watch on a busy deployment. A flood of WebSocket clients, plus a cluster of crons firing together, can exhaust the pool. The symptom is not "the bus is slow". It is intermittent PoolError exceptions in places that look unrelated. The fix is budgeting: size the pool against the real concurrent connection count, including bus connections, and stagger heavy crons so they don't all contend at once. If JSON serialization shows up in bus profiling, v19 also supports the optional orjson library as a drop-in faster encoder.

Migration checklist

  • Remove any client or server code that calls /websocket/update_bus_presence.
  • Update bus-related route overrides from type='json' to type='jsonrpc'.
  • Replace _get_missed_presences_* overrides with a single _after_subscribe_data override.
  • Re-check group-channel notifications now that subscription uses all_group_ids.
  • Update references to the bus model class from ImBus to BusBus (the model name bus.bus is unchanged).
  • Budget your PostgreSQL connection pool against bus connections plus cron concurrency.

The bus rewards being ignored, right up until an upgrade. v19 is a good moment to look at it deliberately, because it is the version that stopped tolerating the old way of talking to it.