google-deepmind / google-deepmind/mujoco

Studio web viewer is unusable: studio.web.headless_ui is missing from the wheel

Open
#3,580 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.