-
Notifications
You must be signed in to change notification settings - Fork 0
[BUG] DuplicateIds crash when clicking NPM packages rapidly #1
Description
Bug Description
Application crashes with DuplicateIds exception when selecting NPM packages in the tree view.
Steps to Reproduce
- Open devops application
- Navigate to NPM tab
- Click on an NPM package (e.g.,
@masonator/get-mcp-keys) - Quickly click on the same or another NPM package
- Application crashes with DuplicateIds error
Expected Behavior
Should display package details without crashing, even when rapidly switching between packages.
Actual Behavior
Application crashes with the following error:
DuplicateIds: Tried to insert a widget with ID 'npm-uninstall-1770075339377', but a widget already
exists with that ID (Button(id='npm-uninstall-1770075339377', classes='-error -style-default',
variant='error')); ensure all child widgets have a unique ID.
Stack Trace:
File: /opt/homebrew/Cellar/devops/0.4.0/libexec/lib/python3.12/site-packages/devops/widgets/detail_panel.py:568
In show_npm_package()
self.mount(
Button(
f"Uninstall {name}",
id=f"npm-uninstall-{int(time.time() * 1000)}",
variant="error",
)
)
Root Cause
The show_npm_package() method in detail_panel.py (line 568) generates button IDs using int(time.time() * 1000). When the method is called multiple times within the same millisecond:
- The timestamp-based ID is identical
_clear_buttons()may not complete removal before new buttons are mounted- Duplicate IDs are created, violating Textual's unique ID requirement
Proposed Fix
Replace timestamp-based IDs with UUID for guaranteed uniqueness:
import uuid
def show_npm_package(self, pkg: dict, pkg_type: str, project_path: str = "") -> None:
self._clear_buttons()
# ... existing code ...
unique_id = str(uuid.uuid4())[:8] # Short unique identifier
if is_outdated and is_global:
self.mount(
Button(
f"Upgrade {name}",
id=f"npm-upgrade-{unique_id}",
variant="success",
)
)
self.mount(
Button(
f"Uninstall {name}",
id=f"npm-uninstall-{unique_id}",
variant="error",
)
)Environment
- Version: 0.4.0
- OS: macOS (Homebrew installation)
- Python: 3.12
- Installation:
brew install jamesrisberg/devops/devops
Impact
Severity: High - Crashes application and prevents NPM package management
Additional Notes
This same pattern exists in other methods throughout detail_panel.py that use int(time.time() * 1000) for button IDs. Consider applying the UUID fix consistently across all button-generating methods to prevent similar crashes.