Skip to content

fix(go2): override ReplayConnection.stop() to avoid AttributeError on hot-reload#3182

Closed
xiaoyaoqilan wants to merge 1 commit into
dimensionalOS:mainfrom
xiaoyaoqilan:fix/replay-connection-stop-attr
Closed

fix(go2): override ReplayConnection.stop() to avoid AttributeError on hot-reload#3182
xiaoyaoqilan wants to merge 1 commit into
dimensionalOS:mainfrom
xiaoyaoqilan:fix/replay-connection-stop-attr

Conversation

@xiaoyaoqilan

Copy link
Copy Markdown
Contributor

Fixes #1967.

Root cause. ReplayConnection intentionally skips UnitreeWebRTCConnection.__init__() (that constructor opens a live WebRTC peer via LegionConnection + connect()), so it never initializes self.stop_timer, self.loop, or self.conn. The inherited stop() therefore raises AttributeError: 'ReplayConnection' object has no attribute 'stop_timer' as soon as GO2Connection.stop() is invoked — which is exactly what restart() does during hot-reload, breaking the reload.

Fix. Override stop() on ReplayConnection to clear the deadman timer slot and otherwise do nothing: a replay connection has no live loop/peer to disconnect, and the module's teardown is handled by the framework. This removes the AttributeError that aborted restart().

Note: this fixes the concrete AttributeError thrown by stop() during hot-reload. If restart() still hangs for reasons unrelated to teardown (e.g. the daemon re-registering the module), that is a separate path and worth a follow-up issue.

… hot-reload

Resolves dimensionalOS#1967.

ReplayConnection intentionally does not call UnitreeWebRTCConnection.__init__() (that constructor opens a live WebRTC peer), so it never gets self.stop_timer / self.loop / self.conn. The inherited stop() then raises 'ReplayConnection' object has no attribute 'stop_timer' when GO2Connection.stop() is called during hot-reload / restart, which is what breaks restart().

Replay connections have nothing to disconnect, so override stop() to just clear the deadman timer slot and let the framework handle the rest.
@greptile-apps

greptile-apps Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds a ReplayConnection-specific stop implementation to avoid accessing uninitialized live-WebRTC state during Go2 hot reload.

  • Clears the replay connection's deadman-timer slot without disconnecting a nonexistent WebRTC peer or event loop.
  • Leaves replay-resource disposal outside the new stop implementation.

Confidence Score: 4/5

The PR appears safe to merge, though the replay-specific stop method should explicitly dispose ReplayConnection-owned resources while avoiding the WebRTC teardown.

The override fixes the reported uninitialized WebRTC attribute access, but it leaves the registered replay store and its SQLite resources undisposed when stop occurs without immediate process termination.

Files Needing Attention: dimos/robot/unitree/go2/connection.py

Important Files Changed

Filename Overview
dimos/robot/unitree/go2/connection.py Adds the hot-reload-safe stop override, but it also skips cleanup of the SqliteStore registered on ReplayConnection.

Reviews (1): Last reviewed commit: "fix(go2): override ReplayConnection.stop..." | Re-trigger Greptile

Comment on lines +262 to +269
def stop(self) -> None:
# A replay connection has no live WebRTC loop or peer to tear down, so
# the inherited UnitreeWebRTCConnection.stop() (which touches self.loop
# and self.conn) does not apply here. Clear the deadman timer slot and
# let the framework handle the rest of the module teardown.
# Fixes 'ReplayConnection' object has no attribute 'stop_timer' raised
# during GO2Connection hot-reload / restart (#1967).
self.stop_timer = None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Replay resources remain undisposed

After the replay property registers its started SqliteStore as a ReplayConnection disposable, this override returns without running CompositeResource.stop(). This leaves the replay store, its SQLite connections, and scheduled replay work open during in-process teardown; skip only the WebRTC-specific cleanup while still disposing replay-owned resources.

Knowledge Base Used: Launching Robots via the CLI and Hardware Vendor Integration

@codecov

codecov Bot commented Jul 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 1 line in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
dimos/robot/unitree/go2/connection.py 50.00% 1 Missing ⚠️
@@            Coverage Diff             @@
##             main    #3182      +/-   ##
==========================================
- Coverage   73.77%   71.32%   -2.46%     
==========================================
  Files        1109     1109              
  Lines      104383   104385       +2     
  Branches     9567     9567              
==========================================
- Hits        77013    74449    -2564     
- Misses      24695    27381    +2686     
+ Partials     2675     2555     -120     
Flag Coverage Δ
OS-ubuntu-24.04-arm 67.71% <50.00%> (+<0.01%) ⬆️
OS-ubuntu-latest ?
Py-3.10 ?
Py-3.11 ?
Py-3.12 ?
Py-3.13 ?
Py-3.14 67.71% <50.00%> (-2.07%) ⬇️
Py-3.14t ?

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/robot/unitree/go2/connection.py 58.42% <50.00%> (+2.95%) ⬆️

... and 29 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@paul-nechifor

Copy link
Copy Markdown
Contributor

This does not fix #1967 . The issue there is present in with UnitreeWebRTCConnection as well, not just ReplayConnection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PY API - Hot-reload of GO2Connection is often broken

2 participants