[GH-ISSUE #246] bug: image previews never render on Windows — frontend sends backslash paths to /api/workspace/files/serve #159

Open
opened 2026-10-18 14:03:27 +00:00 by bsakaguchi · 1 comment
Owner

Originally created by @gucazcode on GitHub (Aug 31, 2026).
Original GitHub issue: https://github.com/open-webui/computer/issues/246

Version: 0.9.21 · OS: Windows 11 · Browser: Chrome

Summary

On Windows, no image ever renders inline. display_file draws the viewer panel — title bar, zoom
controls, 100% — but the image itself is a broken-image icon, and the size readout shows 0 B.

The backend is fine. The frontend builds the URL from a raw Windows path, without converting
backslashes to forward slashes and without URL-encoding
, while the serve endpoint expects
forward slashes — as its own comment says.

Reproduce

  1. On Windows, open a workspace and put a PNG in it.
  2. Ask the agent to display_file that PNG (or open it from the file browser).
  3. The viewer frame appears; the image is broken; size reads 0 B.

Root cause

Frontend — _app/immutable/nodes/2.*.js:

`/api/workspace/files/serve/${n.filePath.startsWith('/') ? n.filePath.slice(1) : n.filePath}`

n.filePath on Windows is e.g. C:\Users\user\workspace\example.png (confirmed — it appears
verbatim as the broken image's alt text). It is interpolated raw: no separator normalisation,
no encoding.

Backend — routers/workspace.py:

@router.get("/serve/{file_path:path}")
async def serve_static(file_path: str):
    # Windows paths arrive as "C:/Users/..." - don't prepend /
    if len(file_path) >= 2 and file_path[1] == ":":
        target = Path(file_path).resolve()
    else:
        target = Path("/" + file_path).resolve()

The comment states the contract — C:/Users/..., forward slashes — which the frontend does
not meet.

Evidence the backend is correct

This URL, in a logged-in browser, renders the image correctly:

http://127.0.0.1:8000/api/workspace/files/serve/C:/Users/user/workspace/example.png

Same file, same session, forward slashes instead of backslashes. So routing, auth, MIME detection
and streaming all work; only the URL the frontend builds is wrong.

Suggested fix

Normalise separators before interpolating, and encode each segment:

const p = n.filePath.replace(/\\/g, '/');
const url = `/api/workspace/files/serve/${p.split('/').map(encodeURIComponent).join('/')}`;

Encoding also fixes paths containing spaces, # or ?, which will fail on every platform.

Related: the bundle already has a resolver, de(), which prefers full_path, falls back to
path, and joins relative paths to the workspace root. The image viewer bypasses it — and de()
does not normalise separators either, so both call sites likely need the same treatment.

Workaround in use

Until this is fixed upstream, we patch the built bundle in place, rewriting the interpolation to
normalise separators. It works, but it is a stopgap: an upgrade replaces the bundle and silently
reverts it
, so the patch has to be re-applied and verified after every update. A fix in the
source is the real answer.

Anyone hitting this before a release can also open the file directly at
/api/workspace/files/view?path=<url-encoded absolute path>, which serves correctly.

Originally created by @gucazcode on GitHub (Aug 31, 2026). Original GitHub issue: https://github.com/open-webui/computer/issues/246 **Version:** 0.9.21 · **OS:** Windows 11 · **Browser:** Chrome ## Summary On Windows, no image ever renders inline. `display_file` draws the viewer panel — title bar, zoom controls, `100%` — but the image itself is a broken-image icon, and the size readout shows `0 B`. The backend is fine. The frontend builds the URL from a raw Windows path, **without converting backslashes to forward slashes and without URL-encoding**, while the serve endpoint expects forward slashes — as its own comment says. ## Reproduce 1. On Windows, open a workspace and put a PNG in it. 2. Ask the agent to `display_file` that PNG (or open it from the file browser). 3. The viewer frame appears; the image is broken; size reads `0 B`. ## Root cause **Frontend** — `_app/immutable/nodes/2.*.js`: ```js `/api/workspace/files/serve/${n.filePath.startsWith('/') ? n.filePath.slice(1) : n.filePath}` ``` `n.filePath` on Windows is e.g. `C:\Users\user\workspace\example.png` (confirmed — it appears verbatim as the broken image's `alt` text). It is interpolated raw: no separator normalisation, no encoding. **Backend** — `routers/workspace.py`: ```python @router.get("/serve/{file_path:path}") async def serve_static(file_path: str): # Windows paths arrive as "C:/Users/..." - don't prepend / if len(file_path) >= 2 and file_path[1] == ":": target = Path(file_path).resolve() else: target = Path("/" + file_path).resolve() ``` The comment states the contract — **`C:/Users/...`, forward slashes** — which the frontend does not meet. ## Evidence the backend is correct This URL, in a logged-in browser, **renders the image correctly**: ``` http://127.0.0.1:8000/api/workspace/files/serve/C:/Users/user/workspace/example.png ``` Same file, same session, forward slashes instead of backslashes. So routing, auth, MIME detection and streaming all work; only the URL the frontend builds is wrong. ## Suggested fix Normalise separators before interpolating, and encode each segment: ```js const p = n.filePath.replace(/\\/g, '/'); const url = `/api/workspace/files/serve/${p.split('/').map(encodeURIComponent).join('/')}`; ``` Encoding also fixes paths containing spaces, `#` or `?`, which will fail on every platform. **Related:** the bundle already has a resolver, `de()`, which prefers `full_path`, falls back to `path`, and joins relative paths to the workspace root. The image viewer bypasses it — and `de()` does not normalise separators either, so both call sites likely need the same treatment. ## Workaround in use Until this is fixed upstream, we patch the built bundle in place, rewriting the interpolation to normalise separators. It works, but it is a stopgap: **an upgrade replaces the bundle and silently reverts it**, so the patch has to be re-applied and verified after every update. A fix in the source is the real answer. Anyone hitting this before a release can also open the file directly at `/api/workspace/files/view?path=<url-encoded absolute path>`, which serves correctly.
Author
Owner

@owui-terminator[bot] commented on GitHub (Aug 31, 2026):

⚠️ Missing Issue Title Prefix

@gucazcode, your issue title is missing a categorising prefix (e.g. bug:, feat:, docs:).

Please update the title to lead with one of:

  • bug: bug report or error
  • feat: feature request or enhancement
  • docs: documentation issue
  • question: usage question
  • help: support request

Example: bug: Login fails when the password contains special characters

<!-- gh-comment-id:5482374004 --> @owui-terminator[bot] commented on GitHub (Aug 31, 2026): # ⚠️ Missing Issue Title Prefix @gucazcode, your issue title is missing a categorising prefix (e.g. `bug:`, `feat:`, `docs:`). Please update the title to lead with one of: - **bug**: bug report or error - **feat**: feature request or enhancement - **docs**: documentation issue - **question**: usage question - **help**: support request Example: `bug: Login fails when the password contains special characters`
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
open-webui/computer#159
No description provided.