Repository navigation
[bug] #1940
Description
Octop 桌面版(Windows)卡在"加载中…"无法进入首次运行向导
影响版本:
v1.0.2b6(beta,Windows amd64 桌面版);稳定版v1.0.1同样依赖系统mimetypes,理论上同样受影响。
客户端:桌面版(Wails v3 + WebView2)。服务端:内嵌 Python 运行时,监听127.0.0.1:8088。
一句话结论:Octop 服务端在 Windows 上用系统mimetypes推断前端.js的 MIME,当注册表把.js登记成text/plain时,所有 ES module 被浏览器拒载,桌面壳与浏览器都永久卡在启动画面。
一、现象
- 安装并启动 Octop 桌面版(Windows amd64)。
- 窗口永久停在"加载中…",不进入首次运行配置向导。
- 即使进程重启、甚至重装,依然卡住。
- 用浏览器直接打开
http://127.0.0.1:8088也卡在同一个"加载中…"画面。
二、复现步骤(可稳定复现)
前置条件(任一即可触发):
- 目标 Windows 机器的注册表
HKEY_CLASSES_ROOT\.js(或HKLM\SOFTWARE\Classes\.js)的Content Type值为text/plain;或 - 该值缺失,且本机 Python
mimetypes对.js的默认回退为text/plain(部分 Windows 环境如此)。
说明:
.js被登记为text/plain并非罕见。某些编辑器、IDE、下载工具、或手动改过文件关联/默认程序的机器上会出现。这意味着该问题不是个别现象,而是会在多台 Windows PC 上随机触发。
复现:
- 在符合上述前置条件的 Windows 上,安装并启动 Octop 桌面版。
- 观察桌面窗口 → 卡在"加载中…"。
- 打开浏览器 DevTools 控制台 → 看到报错:
Failed to load module script: Expected a JavaScript-or-Wasm module script but the server responded with a MIME type of "text/plain". source: http://127.0.0.1:8088/assets/index.<hash>.js curl -I http://127.0.0.1:8088/assets/index.<hash>.js可见响应头:(而content-type: text/plain; charset=utf-8.css/.json/.svg均返回正确类型,唯独.js错。)
三、根因分析
- Octop 前端资源由内嵌的 Python 服务端作为静态文件托管。服务端用 Python 标准库
mimetypes推断Content-Type。 - 在 Windows 上,
mimetypes会读取注册表HKCR\<ext>\Content Type来决定 MIME。当它从注册表(或被注册表污染后的默认值)拿到.js → text/plain时,就会把所有.js以text/plain发出。 - 现代浏览器对
<script type="module">有严格的 MIME 嗅探:要求响应是 JavaScript MIME(如text/javascript、application/javascript),收到text/plain会直接拒绝执行。 - 结果:
index.html能返回(HTML 正常),但所有 ES module 脚本被拒 → React 永远无法挂载 → 启动画面#octop-boot("加载中…")不被移除 → 永久卡住。 - 这与桌面壳(WebView2)无关:用全新 profile 的 Edge 无头浏览器打开同一地址,同样卡在"加载中…",证明故障在服务端 MIME,不在客户端。
关键证据
| 请求 | 服务端 Content-Type |
结果 |
|---|---|---|
/(index.html) |
text/html |
✅ 正常 |
/assets/*.js |
text/plain; charset=utf-8 |
❌ 故障点 |
/assets/*.css |
text/css |
✅ 正常 |
/manifest.json |
application/json |
✅ 正常 |
本机 Python 验证:mimetypes.guess_type('x.js') == ('text/plain', None),且来源确认为注册表 HKLM\SOFTWARE\Classes\.js 的 Content Type = text/plain。给用户分支 HKCU\Software\Classes\.js 写入 Content Type = text/javascript 后,guess_type 即返回 text/javascript,服务端随即正确返回 text/javascript。
四、第二个连环坑:WebView2 磁盘缓存
即使修复了注册表 MIME,仅重启进程仍无法恢复,原因在于 WebView2 的磁盘缓存:
- 首次启动时服务端还在发
text/plain,WebView2 已将.js连同错误类型缓存到磁盘,响应头含cache-control: max-age=31536000, immutable与ETag。 - 修复注册表并重启 Octop 进程后,WebView2 重新请求
.js时携带旧ETag→ 服务端回304 Not Modified→ WebView2 继续使用缓存里的text/plain旧版本 → 仍被浏览器拒载 → 仍卡"加载中"。 - 必须手动删除 WebView2 磁盘缓存目录才能恢复。Octop 桌面壳的缓存位于:
(例如
%APPDATA%\<Octop.exe 名>\EBWebView\Default\Cache\Cache_DataC:\Users\<user>\AppData\Roaming\Octop.exe\EBWebView)。
这意味着普通用户即使"修好了根因",也基本不可能自己推断出"还要清 WebView2 缓存",体验上等于修不好的死循环。
五、连带体验问题:首启密码不可发现
- 首次运行向导的管理员密码写入
C:\Users\<user>\octop-login.txt。 - 但桌面壳卡在"加载中…"时,用户进不去任何界面,也没有任何 UI 提示"密码已生成在 octop-login.txt",导致排查时连"该去哪找密码"都不知道。
- 建议:桌面壳在卡住/出错时,至少在窗口内或日志里提示该文件路径;或向导密码改用 UI 内输入而非隐藏到文件。
六、建议修复
主因(必须修):不要依赖系统 mimetypes/注册表 推断前端静态资源 MIME。
推荐任选其一:
- 在静态文件托管处显式、硬编码前端资源的正确 MIME 映射,例如:
服务端按扩展名查这张表发出
STATIC_MIMES = { ".js": "text/javascript; charset=utf-8", ".mjs": "text/javascript; charset=utf-8", ".css": "text/css; charset=utf-8", ".json": "application/json; charset=utf-8", ".svg": "image/svg+xml", ".wasm": "application/wasm", }
Content-Type,完全不调用mimetypes。 - 或在进程启动早期强制覆盖:
(但方案 1 更稳,因为不依赖注册表是否被后续污染。)
import mimetypes mimetypes.add_type("text/javascript", ".js") mimetypes.add_type("text/javascript", ".mjs")
- 改用自带正确 MIME 的静态服务器(如
whitenoise或框架的send_file(..., mimetype=...))。
缓存坑(建议修):
- 前端 assets 使用 hash 文件名(Octop 已这样做,如
index.<hash>.js)。既然文件名带 hash,内容变更即换名,无需immutable一年缓存;建议把cache-control调成温和值,或在桌面壳检测到启动失败时自动清理 WebView2 缓存。 - 桌面壳提供一个"清除缓存/重置"入口(托盘右键或启动参数),避免用户手动找
%APPDATA%目录。
体验(建议修):
- 卡在加载中/首次运行时,UI 内明确提示向导密码路径(
octop-login.txt)与排查方向。
七、环境信息
- OS:Windows 10 / 11(x64,amd64)
- Octop 版本:
v1.0.2b6(beta);桌面包Octop-desktop-windows-amd64-*.exe - 客户端:Wails v3 + Microsoft Edge WebView2(Runtime 版本 148.x)
- 服务端:内嵌 Python 运行时(随桌面包分发),监听
127.0.0.1:8088 - 数据目录:
C:\Users\<user>\.octop\ - 触发条件:本机注册表
HKCR\.js/HKLM\SOFTWARE\Classes\.js的Content Type为text/plain(或缺失导致默认回退为text/plain)
八、临时绕过(供其他遇到者参考)
- 用户级覆盖注册表(免管理员):
New-ItemProperty -Path "HKCU:\Software\Classes\.js" -Name "Content Type" -Value "text/javascript" -PropertyType String -Force
- 彻底退出 Octop(结束
Octop.exe与内嵌python.exe进程)。 - 删除 WebView2 缓存目录
%APPDATA%\<Octop.exe 名>\EBWebView。 - 重新启动 Octop。
英文摘要(如向上游提英文 issue 可附上)
Title: Desktop app (Windows) stuck on "Loading…" — server serves .js with text/plain MIME due to Windows registry mimetypes
Octop's embedded Python server uses mimetypes.guess_type() to set Content-Type for frontend static assets. On Windows, mimetypes reads the registry (HKCR\.js → Content Type). When that value is text/plain (which happens on many Windows machines, e.g. after certain editors/tools touch file associations), all ES module .js files are served as text/plain. Browsers strictly reject text/plain module scripts, so React never mounts and the desktop shell (WebView2) — and even a plain browser at 127.0.0.1:8088 — stay stuck on the "加载中…" boot screen forever.
Repro: install/run Octop desktop on a Windows box whose registry .js = text/plain → window hangs on boot → console shows Failed to load module script ... MIME type of "text/plain".
Secondary trap: WebView2 caches the bad .js on disk (immutable, 1y). Fixing the registry + restarting the process is not enough — the WebView2 disk cache (%APPDATA%\<Octop.exe>\EBWebView) must be deleted too.
Fix建议: hardcode frontend MIME map (.js→text/javascript) instead of relying on system mimetypes; avoid immutable caching for hashed assets; expose a "clear cache" entry in the desktop shell; surface the first-run wizard password path in the UI.
Activity