hermes - ✅(Solved) Fix [Bug]: Dashboard blank page on native Windows because JS bundles are served as text/plain [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#28987Fetched 2026-05-20 04:00:45
View on GitHub
Comments
0
Participants
1
Timeline
4
Reactions
0
Participants
Timeline (top)
labeled ×3cross-referenced ×1

Error Message

Additional Logs / Traceback

Root Cause

hermes_cli/web_server.py relies on Python/Starlette MIME guessing for built frontend assets. On Windows, Python's mimetypes can inherit registry overrides where .js resolves to text/plain. That makes /assets/*.js responses carry the wrong Content-Type, so browsers reject the Vite entry bundle and the dashboard never mounts.

I also checked the current upstream main, and it still appears to be missing an explicit .js/.mjs MIME normalization.

Fix Action

Fixed

PR fix notes

PR #28996: fix(dashboard): force JavaScript MIME on built SPA assets (#28987)

Description (problem / solution / changelog)

What does this PR do?

On native Windows hermes dashboard renders as a blank page: the HTML shell loads fine, but the Vite ESM bundle comes back with Content-Type: text/plain; charset=utf-8 and Chrome refuses the module script under strict MIME checking ("Failed to load module script: Expected a JavaScript-or-Wasm module script…").

Root cause. Starlette's StaticFiles / FileResponse both pick Content-Type from mimetypes.guess_type. Python's mimetypes.init() reads the Windows registry, and many installers (older IIS / Visual Studio toolchains, even some antivirus suites) pollute HKEY_CLASSES_ROOT\.js with Content Type = text/plain. Python faithfully honours that, the browser refuses the module, the SPA never mounts.

Fix. At hermes_cli.web_server import time, explicitly override the strict mimetypes map for the asset extensions we actually serve — text/javascript for .js / .mjs / .cjs (per RFC 9239), plus correct values for .css, .json, .map, .svg, .wasm, .webp, .webmanifest, .woff, .woff2, .ico. mimetypes.add_type(..., strict=True) writes directly into the types_map dict that guess_type consults first, so our values win regardless of what the registry says. Idempotent.

Related Issue

Fixes #28987

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • hermes_cli/web_server.py — Add _WEB_ASSET_MIME_OVERRIDES (13 extensions) and _normalize_web_asset_mime_types(); invoke it once at module import. Long block comment documents the Windows registry corner case so the next maintainer doesn't accidentally "simplify" the override away.
  • tests/hermes_cli/test_web_server_asset_mime.py — 23 new regression tests in four classes:
    • TestMimeNormalizationFunction — unit-level: simulate the Windows registry poison by writing text/plain into mimetypes.types_map[".js"], then assert the helper recovers a JavaScript MIME. Parametrised once-per-extension so a future drive-by removal is identified by name.
    • TestMimeNormalizationAtImport — reload hermes_cli.web_server from scratch under a poisoned map and assert the module-level call self-heals the import order.
    • TestStaticFileServingEndToEnd — drive a real starlette.testclient.TestClient against a synthetic Vite-style WEB_DIST. Asserts /assets/index-abc.js returns a JavaScript MIME and / still returns text/html (no accidental downgrade).
    • TestMimeOverrideRegistryShape — structural guards: every entry maps to a valid type/subtype pair and every extension starts with ..

How to Test

Reproduce the bug on Windows main:

hermes dashboard --no-open
# Browse to http://127.0.0.1:9119/ — blank page.
# Check the response header:
Invoke-WebRequest -UseBasicParsing http://127.0.0.1:9119/assets/index-<hash>.js |
  Select-Object -ExpandProperty Headers
# Content-Type: text/plain; charset=utf-8   ← the bug

After this PR:

hermes dashboard --no-open
# Dashboard renders normally.
Invoke-WebRequest -UseBasicParsing http://127.0.0.1:9119/assets/index-<hash>.js |
  Select-Object -ExpandProperty Headers
# Content-Type: text/javascript; charset=utf-8   ← fixed

Automated coverage (portable — reproduces the failure mode on any OS by directly poisoning the mimetypes map):

scripts/run_tests.sh tests/hermes_cli/test_web_server_asset_mime.py -q
# 23 passed in 5.45s

Quick manual sanity check on macOS:

.venv/bin/python -c "
import mimetypes
mimetypes.types_map['.js'] = 'text/plain'        # simulate Windows
print('before:', mimetypes.guess_type('a.js'))
import hermes_cli.web_server                     # trigger the fix
print('after: ', mimetypes.guess_type('a.js'))
"
# before: ('text/plain', None)
# after:  ('text/javascript', None)

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(dashboard):, test(dashboard):)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix
  • I've run the relevant tests locally and they pass
  • I've added tests for my changes (23 new regression tests)
  • Tested on my platform: macOS 15.2 (Darwin 24.6.0). Test design reproduces the Windows-specific failure portably by poisoning the strict mimetypes map.

Documentation & Housekeeping

  • N/A — no user-facing config / docs touched (the fix is invisible to operators who run a working setup; only fixes a regression for the affected install profile)
  • N/A — no config keys added or changed
  • N/A — no architecture or workflow changes
  • Cross-platform — the fix is harmless on Linux/macOS (their stock mimetypes already report text/javascript for .js; we still upgrade .mjs and .map which aren't in Python's defaults)
  • N/A — no tool descriptions / schemas changed

Screenshots / Logs

Browser console before the fix (per #28987):

Failed to load module script: Expected a JavaScript-or-Wasm module script
but the server responded with a MIME type of "text/plain". Strict MIME
type checking is enforced for module scripts per HTML spec.

Response headers before / after the fix (curl -I on a built Vite bundle):

# Before
HTTP/1.1 200 OK
content-type: text/plain; charset=utf-8

# After
HTTP/1.1 200 OK
content-type: text/javascript; charset=utf-8

Changed files

  • hermes_cli/web_server.py (modified, +56/-0)
  • tests/hermes_cli/test_web_server_asset_mime.py (added, +292/-0)

Code Example

Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/plain".

---

Invoke-WebRequest -UseBasicParsing http://127.0.0.1:9119/assets/index-<hash>.js |
     Select-Object -ExpandProperty Headers

---

Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/plain". Strict MIME type checking is enforced for module scripts per HTML spec.
RAW_BUFFERClick to expand / collapse

Bug Description

On native Windows, hermes dashboard serves the HTML shell but the page stays blank because the main JS bundle is returned as text/plain; charset=utf-8 instead of application/javascript.

Steps to Reproduce

  1. On native Windows 11, run hermes dashboard --no-open (or run it in the background).
  2. Open http://127.0.0.1:9119/ in a browser.
  3. Observe that the page is blank.
  4. Open the browser console and note:
    Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/plain".
  5. Fetch the bundle directly and inspect the headers:
    Invoke-WebRequest -UseBasicParsing http://127.0.0.1:9119/assets/index-<hash>.js |
      Select-Object -ExpandProperty Headers
    The response Content-Type is text/plain; charset=utf-8.

Expected Behavior

The dashboard JS bundles should be served with a JavaScript MIME type and the SPA should render normally.

Actual Behavior

The server returns the HTML shell, but the JS bundle is served as text/plain, the browser refuses to execute the module script, and the dashboard remains blank.

Affected Component

Other (dashboard/web UI)

Messaging Platform

N/A (CLI only)

Debug Report

Not attached. The failure was isolated to the frontend asset Content-Type, so I did not include a broader debug dump.

Operating System

Microsoft Windows 11 Pro 10.0.22631 (Build 22631)

Python Version

3.11.14

Hermes Version

Hermes Agent v0.14.0 (2026.5.16)

Additional Logs / Traceback

Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/plain". Strict MIME type checking is enforced for module scripts per HTML spec.

Root Cause Analysis

hermes_cli/web_server.py relies on Python/Starlette MIME guessing for built frontend assets. On Windows, Python's mimetypes can inherit registry overrides where .js resolves to text/plain. That makes /assets/*.js responses carry the wrong Content-Type, so browsers reject the Vite entry bundle and the dashboard never mounts.

I also checked the current upstream main, and it still appears to be missing an explicit .js/.mjs MIME normalization.

Proposed Fix

Normalize the dashboard asset MIME types at server startup instead of relying on host OS MIME mappings, at minimum:

  • .js -> application/javascript
  • .mjs -> application/javascript

A regression test could assert that requesting a built /assets/*.js file from the dashboard returns Content-Type: application/javascript.

PR Ready

I'd be willing to submit a PR for this.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Bug]: Dashboard blank page on native Windows because JS bundles are served as text/plain [1 pull requests, 1 participants]