thousands-dusk-35277
06/22/2026, 4:33 PM<http://ghcr.io/all-hands-ai/openhands:latest|ghcr.io/all-hands-ai/openhands:latest>) behind a vLLM backend. The CLI works perfectly, but the Web UI shows a React hydration error (#418) and keeps showing loading when I open a new conversation, and returns a 500 error when trying to delete a conversation. I have tried clearing local storage, incognito mode, and bypassing Cloudflare. Any ideas?"thankful-dusk-15577
06/23/2026, 10:36 AMthousands-dusk-35277
06/23/2026, 7:35 PM<http://ghcr.io/openhands/agent-canvas:latest|ghcr.io/openhands/agent-canvas:latest>), trying to get a live view of the agent's own browsing session via the built-in VNC/Desktop feature.
What I've confirmed so far:
• Setting`OH_ENABLE_VNC=true`at the container level + publishing port 8002 brings up a working noVNC desktop (XFCE, connects fine).
• In`software-agent-sdk`:`desktop_service.py`starts TigerVNC/noVNC, and`browser_use/impl.py`checks`OH_ENABLE_VNC`to force browser-use into non-headless mode.
Two things I haven't confirmed, and would love a quick pointer on before I dig further:
1. Is the`OH_ENABLE_VNC`check in`browser_use/impl.py`reading the container-level env var directly, or does it expect a per-conversation/session setting passed through from the frontend? Trying to rule out "the container thinks VNC is on, but this particular conversation never actually requested headed mode."
2. Once browser-use does launch headed and targets the right`DISPLAY`, is there anything else needed for its window to actually appear on the XFCE desktop noVNC is streaming — a window manager mapping it, a specific screen/display number — or should it just show up on its own?
Happy to share exact image/build info if useful. Mainly trying to confirm whether there's one more piece of config I'm missing before assuming this needs an actual fix.thousands-dusk-35277
06/23/2026, 9:41 PMthankful-dusk-15577
06/24/2026, 6:24 AMthankful-dusk-15577
06/24/2026, 6:25 AMthousands-dusk-35277
06/24/2026, 1:05 PMDISPLAY=:1, no --headless flag), the panel stayed blank. Pulling the raw observation event for a browse action showed why: screenshot_data: null, and the paired action had "include_screenshot": false. The agent's accurate, detailed page descriptions were coming entirely from a structured text observation (url/title/tabs/interactive_elements) — never from vision. It wasn't hallucinating; it genuinely never needed a screenshot to do its job.
Root cause, with the exact source:
# openhands-tools/openhands/tools/browser_use/definition.py
include_screenshot: bool = Field(
default=False,
description="Whether to include a screenshot of the current page. Default: False",
)
The tool's own description tells the model the default is False. The model was following its instructions correctly — this isn't unexpected behavior, it's the documented default doing exactly what it says.
The fix, found rather than built: Agent Canvas has a global settings field — agent_settings.agent_context.load_user_skills — defaulted to false. Flipping it to true, then adding a global skill at `~/.agents/skills/browser-screenshots/SKILL.md`:
---
name: browser-screenshots
triggers:
- browser
- browse
- website
- webpage
- navigate
---
When using the browser_get_state tool, always set include_screenshot=true so
a human watching the Browser panel can see the page.
closed the loop. Verified in a brand-new conversation, with a prompt containing none of the trigger words explicitly about screenshots — the resulting raw event showed include_screenshot: true set automatically, with real screenshot data attached.
Worth knowing if you're comparing this to similar tools: Devin's own docs explicitly tell users to prompt for screenshot verification too — this isn't unique automatic behavior on their end either. The actual gap was discoverability of load_user_skills as the mechanism for global, repo-independent default instructions, not a missing capability.
Two smaller things worth flagging, not chasing further myself right now:
1. Recurring WebSocket disconnects on the events socket (/sockets/events/{id}) across long sessions — multiple different conversation IDs, sustained over 2+ hours, not tied to a single container restart. Possibly a proxy/tunnel idle-timeout interacting with long-lived connections.
2. The event-search endpoint appears to throw an internal error specifically on conversations that carry real screenshot data — noticed after the fix above started working, so possibly related to payload size on that endpoint.
Happy to share more detail on any of this if useful to anyone hitting the same thing.