google-deepmind / google-deepmind/mujoco
Studio web viewer is unusable: studio.web.headless_ui is missing from the wheel
- Dominant language
- C++
- Stars
- 15.2k
- Forks
- 1.8k
- Avg merge
- 10d 16h
- Merged PRs (30d)
- 25
Description
### Intro
Hi!
I'm from AWS. I use MuJoCo for headless physics simulation and offscreen rendering distributed across many workers, and I have been trying Studio's Python bindings to add an in-viewer UI.
### My setup
Reproduced on both:
- MuJoCo 3.13.0 (PyPI wheel), Python 3.11.5, arm64, macOS 26.6.2
- MuJoCo 3.13.0 (PyPI wheel), Python 3.14.4, x86_64, Ubuntu 26.04
API: Python.
### What's happening? What did you expect?
`web_viewer.py` imports `headless_ui` from `mujoco.experimental.studio.web`, but
that module is not present in the wheel. Selecting the `web` or `webgl` graphics
mode therefore fails.
`viewer_protocol.GFX_MODES` advertises both modes:
```python
('classic', 'classic_headless', 'opengl', 'opengl_headless',
'opengl_software', 'vulkan', 'vulkan_software', 'web', 'webgl')
```
so they are selectable but cannot work. I expected either a working web viewer, or
for the unavailable modes not to be offered.
The failure also arrives awkwardly. It happens on the viewer thread, so
`launch_passive` returns a handle and `is_running()` reports `True` at first,
turning `False` only later:
```
launch_passive returned: ViewerHandle is_running: True
Exception in thread Thread-1 (target):
Traceback (most recent call last):
...
File ".../studio/launch_web.py", line 39, in run_web_viewer
from mujoco.experimental.studio import web_viewer
File ".../studio/web_viewer.py", line 51, in
from mujoco.experimental.studio.web import headless_ui
ImportError: cannot import name 'headless_ui' from
'mujoco.experimental.studio.web' (unknown location)
is_running after 5s: False
```
Process exit status is 0, so a script that does not poll `is_running()` will not
notice at all.
### Cause
The shipped `web` package contains only `web_server.py`:
```
$ ls site-packages/mujoco/experimental/studio/web/
__pycache__ web_server.py
```
There is also no `__init__.py`, which is why the message says "unknown location".
### Why it matters beyond the web viewer
On macOS this is currently the only Studio backend that avoids the main-thread
window problem (filed separately), because rendering happens in the browser. With
it unavailable, there is no working way to run Studio from Python on macOS without
a workaround.
### Suggested fix
Ship `headless_ui`, and an `__init__.py` for the `web` package. Alternatively,
remove `web` and `webgl` from `GFX_MODES` until they ship, so the failure surfaces
at configuration time rather than as a traceback on a background thread.
### Steps for reproduction
1. `pip install mujoco==3.13.0`
2. Confirm the module is absent: `python -c "from mujoco.experimental.studio.web import headless_ui"`
3. Or go through the documented path: save `minimal.xml` and `repro.py` below into
the same directory and run `python repro.py`
### Minimal model for reproduction
The model is not material to this bug, since the failure occurs during viewer
startup before the model is used. Included for completeness, loadable as-is with no
binary assets:
```xml
```
### Code required for reproduction
Two lines are enough:
```python
from mujoco.experimental.studio.web import headless_ui
```
Or via the public API, `repro.py`:
```python
import time
import mujoco
from mujoco.experimental.studio import launch_passive, viewer_protocol
model = mujoco.MjModel.from_xml_path("minimal.xml")
handle = launch_passive.launch_passive(
viewer_protocol.ViewerConfig(title="studio web repro", gfx="web", http_port=8099)
)
print("launch_passive returned:", type(handle).__name__, "is_running:", handle.is_running())
time.sleep(5) # give the viewer thread time to fail
print("is_running after 5s:", handle.is_running())
```
The `time.sleep` matters: without it the script exits before the viewer thread
reaches the failing import, and nothing is printed at all.
### Confirmations
- [x] I searched the [latest documentation](https://mujoco.readthedocs.io/en/latest/overview.html) thoroughly before posting.
- [x] I searched previous [Issues](https://github.com/google-deepmind/mujoco/issues) and [Discussions](https://github.com/google-deepmind/mujoco/discussions), I am certain this has not been raised before.
Contributor guide
Research direction
Begin with studio/web_viewer.py and viewer_protocol.GFX_MODES, then trace how studio/web/web_server.py is packaged into the PyPI wheel. Reproduce with the direct import or the web graphics path. Done means the advertised web modes either import and launch successfully with the shipped module or are no longer offered, with the resulting behavior covered by a test.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- backend, build-system
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 68/100