OpenHands / OpenHands/enterprise

[Bug]: V1 Web UI connects to agent-server via dynamic host port behind reverse proxy (Traefik), causing persistent "Network Error" / conversation stuck

Open
#37 1 comment 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
4
Forks
2
Avg merge
1d 22h
Merged PRs (30d)
101

Description

Is there an existing issue for the same bug?
  • I have searched existing issues and this is not a duplicate.
Bug Description

Title: [Bug]: V1 Web UI connects to agent-server via dynamic host port behind reverse proxy (Traefik), causing persistent "Network Error" / conversation stuck

Is there an existing issue for the same bug?

I have searched the existing issues. Related/possibly duplicate: OpenHands/OpenHands#12519, OpenHands/OpenHands#12403, OpenHands/OpenHands#12229.

Describe the bug

When deploying OpenHands V1 behind a reverse proxy (Traefik, via Dokploy) on a VPS, the application loads correctly, conversations are created, sandboxes spawn successfully, and skills load — but the conversation never becomes usable. The UI shows "Network Error" in the Changes panel and the message input stays "Disconnected" / "Waiting for sandbox".

Backend logs are completely clean (all 200 OK, no errors after the auth fix below).

The root cause is visible in the browser DevTools Network tab: the frontend issues requests directly to the agent-server using a dynamic host port instead of routing them through the reverse-proxied domain. Example failed request URL:

https://openhands.example.com:42031/api/git/changes?path=%2Fworkspace%2Fproject

The port (42031) is the randomly-assigned host port Docker maps to the agent-server container's 8000. This port:

is not in DNS,
is not exposed/routed by Traefik (only 443 → app:3000 is),
changes on every conversation,

so the browser request fails with a CORS preflight failure / connection error → "Network Error".

This matches the architecture limitation already documented in OpenHands/OpenHands#12403 ("Bridge network uses dynamic host ports, making it incompatible with proxy patterns") and the symptom in OpenHands/OpenHands#12519 ("Web UI connects to runtime WebSocket via localhost:<random_port> instead of remote host").

Current behavior

App UI loads fine over https://openhands.example.com (Traefik → app container :3000).
A new conversation is created (POST /api/v1/app-conversations → 201).
agent-server container spawns and reports healthy; 50 skills load successfully.
Browser then tries to reach the agent-server directly at https://openhands.example.com:<dynamic_port>/... and fails.
WebSocket connections to the app domain succeed (Finished), but the direct-to-agent-server XHR/preflight requests fail.
Result: "Network Error" + "Disconnected", conversation unusable.

Expected behavior

When OH_WEB_URL / a reverse-proxy deployment is configured, the frontend should route all agent-server traffic through the main app domain (proxied by the app server), never directly to a dynamic host port. A reverse-proxy deployment should not require the browser to reach arbitrary host ports.

How to reproduce

Deploy OpenHands V1 via Docker Compose behind Traefik (Dokploy), exposing only the app on a domain with HTTPS (port 3000 internal).
Configure an LLM (Anthropic Claude) in Settings.
Start a new conversation ("Start from Scratch").
Observe "Network Error" / "Disconnected".
Open DevTools → Network: failed requests target https://:<dynamic_port>/api/git/changes?....

OpenHands Installation

Docker Compose (self-hosted), behind Traefik reverse proxy managed by Dokploy.

OpenHands Version

App image: docker.openhands.dev/openhands/openhands:latest (SDK v1.27.0)
Agent server image: ghcr.io/openhands/agent-server:1.27.0-python

Operating System

Debian (VPS), Docker / Dokploy stack.

Logs, error messages, screenshots

Browser DevTools (Network tab), failed request General section:

Request URL: https://openhands.example.com:42031/api/git/changes?path=%2Fworkspace%2Fproject
Referrer Policy: strict-origin-when-cross-origin

agent-server container inspect (dynamic port mapping):

"8000/tcp":[{"HostIp":"","HostPort":"33587"}],
"8001/tcp":[{"HostIp":"","HostPort":"57999"}],
"8011/tcp":[{"HostIp":"","HostPort":"44743"}],
"8012/tcp":[{"HostIp":"","HostPort":"43617"}]

Note: the agent-server containers are always created on the default bridge (172.17.0.x), even when the main app is attached to custom Docker networks — consistent with OpenHands/OpenHands#12229 (the dynamically-created oh-agent-server-* containers don't inherit the app's network / extra_hosts).

Additional context

To get this far, two non-documented fixes were required:

Webhook 401 Unauthorized: The agent-server signs webhook callbacks to the app (http://host.docker.internal:3000/api/v1/webhooks/...) using OH_SESSION_API_KEYS_0, but the app rejected them with 401 because it had no matching secret (no /.openhands/.jwt_secret). Setting a fixed JWT_SECRET env var on the app container resolved the 401s. It would help to document this for reverse-proxy / Compose deployments.
Dynamic-port problem (this issue): even with auth fixed and the conversation/skills working server-side, the browser cannot reach the agent-server because of the dynamic host port. APP_MODE=oss did not change this behavior. network_mode: host is not a viable workaround (breaks health checks per OpenHands/OpenHands#12229 / OpenHands/OpenHands#11907).

A reverse-proxy-friendly mode — where the app server proxies all agent-server/sandbox traffic under the main domain (e.g. /<conversation_id>/... paths) instead of the browser connecting to dynamic ports — would make V1 usable behind Traefik/nginx/Caddy, which is the standard self-hosting setup.

Expected Behavior

No response

Actual Behavior

No response

Steps to Reproduce

No response

OpenHands Installation Method

Other

If you selected "Other", please specify

No response

OpenHands Version

No response

Version Confirmation
  • I have confirmed this bug exists on the LATEST version of OpenHands
Model Name

No response

Operating System

None

Browser (if using web UI)

No response

Logs and Error Messages

No response

Screenshots and Additional Context

No response

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

The issue names /api/v1/app-conversations, /api/git/changes, and the app/agent-server Docker Compose boundary as entry points; start by tracing how the frontend builds agent-server URLs and how the app exposes proxy routes. Reproduce behind Traefik and inspect browser Network requests. Done means agent-server and sandbox traffic stays on the main HTTPS domain without dynamic host ports.

Written by the indexing model from the issue text.

Assessment

Tech stack
docker, python
Domain
backend-api-design, cloud, infrastructure
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.