Skip to content

[Bug]: Dashboard returns 404 on Windows with pnpm global install because control-ui files are hardlinked #40108

@LeapSurge

Description

@LeapSurge

Bug type

Regression (worked before, now fails)

Summary

Dashboard root returns 404 on Windows when OpenClaw is installed globally with pnpm, because dist/control-ui files are installed as hardlinks and the static UI files are not served.

Steps to reproduce

  1. On Windows, install OpenClaw globally with:
    pnpm add -g openclaw@latest --config.auto-install-peers=false
  2. Start the gateway.
  3. Run:
    openclaw dashboard --no-open
  4. Open the printed dashboard URL, for example:
    http://127.0.0.1:18789/#token=...
  5. Observe that:
    • http://127.0.0.1:18789/health returns 200
    • http://127.0.0.1:18789/ready returns 200
    • http://127.0.0.1:18789/__openclaw/control-ui-config.json returns 200
    • http://127.0.0.1:18789/ returns 404
    • http://127.0.0.1:18789/index.html returns 404
  6. Inspect the installed UI file and note that it is a hardlink:
    ...node_modules/openclaw/dist/control-ui/index.html

Expected behavior

The dashboard URL printed by openclaw dashboard --no-open should open the Control UI successfully, and / should serve the dashboard HTML.

Actual behavior

The gateway is healthy and Control UI config routes are present, but the dashboard root returns 404.

Observed:

  • /health -> 200
  • /ready -> 200
  • /__openclaw/control-ui-config.json -> 200
  • / -> 404
  • /index.html -> 404

As a workaround, copying dist/control-ui to a normal directory and setting gateway.controlUi.root to that directory makes / and /index.html return 200.

OpenClaw version

2026.3.7 (42a1394)

Operating system

Windows 10 x64 (10.0.19045)

Install method

Global install with pnpm: pnpm add -g openclaw@latest --config.auto-install-peers=false

Logs, screenshots, and evidence

Gateway status was healthy while dashboard root still failed.

HTTP evidence:
- `http://127.0.0.1:18789/health` -> `200`
- `http://127.0.0.1:18789/ready` -> `200`
- `http://127.0.0.1:18789/__openclaw/control-ui-config.json` -> `200`
- `http://127.0.0.1:18789/` -> `404`
- `http://127.0.0.1:18789/index.html` -> `404`

The installed file is a hardlink:

PowerShell:


Get-Item "D:\pnpm-global\5\.pnpm\[email protected]_@[email protected]\node_modules\openclaw\dist\control-ui\index.html" | Format-List FullName,Attributes,LinkType,Target


Output included:
LinkType : HardLink

Node evidence:

const fs = require("fs");
const st = fs.lstatSync("D:/pnpm-global/5/.pnpm/[email protected]_@[email protected]/node_modules/openclaw/dist/control-ui/index.html");
console.log({ isSymbolicLink: st.isSymbolicLink(), nlink: st.nlink, size: st.size });

Output:


{ isSymbolicLink: false, nlink: 2, size: 692 }

Workaround that fixed it:

1. Copy dist/control-ui to a normal directory
2. Set:

{
  "gateway": {
    "controlUi": {
      "enabled": true,
      "root": "C:\\Users\\lp\\.openclaw\\control-ui"
    }
  }
}

3. Restart gateway

After that:

/ -> 200
/index.html -> 200
/__openclaw/control-ui-config.json -> 200

Impact and severity

  • Affected users/systems/channels:
    Windows users installing OpenClaw globally with pnpm, especially when dashboard access is needed.
  • Severity:
    Blocks dashboard/control UI usage, but gateway and channels can still run.
  • Frequency:
    Deterministic in this environment.
  • Consequence:
    Users cannot access the dashboard even though the gateway is healthy, which makes onboarding, configuration, and debugging much harder.

Additional information

This does not look like a gateway auth problem or a missing asset build problem.

The Control UI config endpoint is present and returns 200, so the gateway is mounting Control UI-related routes. The failure appears specific to serving static files from the pnpm-installed dist/control-ui directory.

This looks like a Windows + pnpm hardlink interaction with OpenClaw's static file serving / safe-open logic.

I did not verify whether the same issue occurs with:

  • npm global install
  • the Windows installer
  • WSL
  • a source checkout

In this environment, overriding gateway.controlUi.root to a copied non-hardlinked directory is a reliable workaround.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingregressionBehavior that previously worked and now fails

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions