- 移除 GovAI, nomifun-tauri, 算力盒子 的 submodule 引用 - 添加所有子项目的完整源代码 - 保留原始 .git 为 .git.bak 备份
12 KiB
疑难排查
运行 NomiFun 时可能遇到的症状,以及它们背后真实的成因。如果你遇到的
问题不在本表里,源码就是最快的参考——本页描述的每一个行为都对应
crates/backend/ 下的某个具体文件。
后端端口 / 连接问题
nomifun-web: invalid --host '<value>'
host 参数必须能被解析为 IP 地址(127.0.0.1、0.0.0.0、某个具体
网卡的 IP)。localhost 这样的主机名不会被解析——nomifun-web 会
直接以这条信息快速失败,而不是稍后再抛出晦涩的 socket 错误。请传入
一个 IP 字面量。
配置端口上出现 address already in use
有别的进程占着同一个端口。Web 宿主默认使用 8787
(NOMIFUN_WEB_PORT)。桌面外壳没有这个问题——它启动时会向 OS 索要
一个空闲的 localhost 端口,然后通过 IPC 告知渲染端。
在 Linux/macOS 上找出占用者:lsof -i :8787。Windows 上:
Get-NetTCPConnection -LocalPort 8787。要么把它杀掉,要么修改
--port / NOMIFUN_WEB_PORT。
浏览器无法在非 loopback 地址上访问服务
nomifun-web 默认绑定 127.0.0.1。要接受 LAN/VPN 流量,需要传入
--host 0.0.0.0 或设置 NOMIFUN_WEB_HOST=0.0.0.0。如果该主机可被较大范围访问,
请先预置管理员或完成首次设置。在 Windows / macOS 宿主上还要检查防火墙——OS
可能默默丢弃连接。
如果目标只是从手机或同一 LAN 上的其他设备进行远程访问, WebUI 远程访问 通常比完整的服务 部署更省事。
首次启动管理员与登录问题
启动服务后 GET /api/auth/status 返回 needs_setup: true
这是全新安装且没有设置 NOMIFUN_ADMIN_PASSWORD 时的预期状态。第一个
访问浏览器的人输入的用户名 + 密码会通过 POST /api/auth/setup 成为
管理员。打开 URL,填写表单,登录即可完成。
如果希望在服务对外可达之前关闭这个时间窗口,请在首次启动前设置
NOMIFUN_ADMIN_PASSWORD(可选地再设置 NOMIFUN_ADMIN_USERNAME)。
/api/auth/setup 返回 409 Conflict
管理员已经存在了。setup 端点只能调用一次。请改用 POST /login;
如果在自托管实例上忘了密码,可通过
WebUI 远程访问 描述的仅本地 WebUI
流程恢复。
登录看起来成功,但下一个请求返回 401
绝大多数情况是 TLS 反向代理后的 cookie 问题。Secure 标记只在
NOMIFUN_HTTPS=true 时才会被加到 cookie 上。在没有该标记的 HTTPS
响应中,浏览器会直接拒收 cookie,下一个请求于是没有会话。设置
NOMIFUN_HTTPS=true 然后重新加载即可。
第二个原因:服务器时钟漂移。如果系统时钟差得太远,签发它的同一台 服务器都会把 JWT 当作过期。请确认 NTP 在跑。
修改密码时密码明明对,却报 Current password is incorrect
该端点会用恒定时间的 bcrypt 比对存储的哈希。如果你怀疑数据损坏:
停止服务、备份数据目录,然后查看 system_default_user.password_hash
列。可以做精细的修复(在本地模式下使用
/api/auth/internal/users/{id}/password),但最简单的路径是从备份
恢复或重新初始化。
用一个含糊的校验错误拒绝"用户名/密码"
校验器拒绝一小撮显而易见的模式——长度小于 8 的密码、常见词典词、
不在 [a-zA-Z0-9_-] 中的用户名,或者以 -/_ 开头/结尾的用户名。
请换一个。
CSRF 错误
在 POST/PUT/PATCH/DELETE 上出现 403 CSRF token validation failed
nomifun-csrf-token cookie 的值必须与 x-csrf-token 请求头匹配。
中间件会在第一个没带它的响应中自动设置该 cookie,因此一个新加载的
SPA 会在它的第一个 GET 请求中拿到。这通常因为以下几种原因失败:
- 客户端假设服务处于无鉴权本地模式,但服务实际运行在已鉴权模式下
(或反过来)。
nomicore --local与nomifun-web --insecure-no-auth会跳过 CSRF;普通nomifun-web需要它。桌面外壳使用TrustLocalToken, 自己的 WebView 正常不应看到 CSRF 失败,除非注入的本地信任 header/cookie 流程被破坏。 - 反向代理剥掉了 cookie 或重写了
Set-Cookie。Caddy/nginx 的标准配置 不会动它们;自定义 rewrite 规则可能会。 - 浏览器的第三方 cookie 屏蔽影响到了部署域名。
/login、/api/auth/setup、/api/auth/qr-login 对 CSRF 豁免;CSRF
仅作用于登录后的状态变更类路由。
WebSocket 断连
连接立刻以 1008 关闭
1008 是"策略违规"——服务端在两种特定情况下使用:
- "no token provided" —— WebSocket 升级请求没有携带 JWT。
- 一条
auth-expired事件后接关闭——token 存在但无效或已过期。
这两种通常都是 token 过期导致。通过 GET /api/ws-token 刷新 token
后重连即可。如果你在登录后立刻看到这一现象,请确认 cookie 是否被
正常透传(参见上面 cookie 不生效的小节),并确认
Sec-WebSocket-Protocol(或你使用的请求头)原样到达了服务端。
WebSocket 连上之后悄悄不再收到事件
服务端每 30 秒 ping 一次,60 秒未响应则视为客户端已死。如果网络静默 地断掉了连接(移动 NAT、强制门户、不靠谱的代理),在服务端清理它 之前客户端侧仍显示已连接。客户端应当负责重连;SPA 会自动重连。如果 你写了自己的客户端,请在 close 事件上实现指数退避重连。
"Agent CLI not found" 与 bun 相关问题
会话立即以 "agent not available" / "command not found" 失败
智能体引擎会派生 ACP 智能体 CLI(claude、codex、gemini、
nomi、codebuddy 等),它们必须出现在进程的 PATH 里。
进程 PATH 在启动时被增强(nomifun_runtime::enhance_process_path),
但若二进制位置不寻常,仍可能被错过。
跑一下 doctor:
nomicore doctor
它会填充智能体注册表,逐个探测 $PATH 上的每个 CLI,并打印一张按
智能体维度的可用性表格。务必在与启动应用相同的 shell 中运行它,
以看到应用真正看到的内容。如果某个智能体缺失,请安装其 CLI 或把它
的 bin 目录加入 PATH 后重启。
在 systemd 下:bun: command not found
智能体引擎要求 bun ≥ 1.3.13。一个 nologin 的系统账户看不到
~/.bun/bin/;请把 bun 装到系统级(sudo install ~/.bun/bin/bun /usr/local/bin/bun),或用 NOMIFUN_EMBED_BUN=1 构建以将 bun 打包
进二进制——它会在首次运行时把自己解压到数据目录。已写好的食谱见
Web 服务部署。
安装后用 sudo -u nomifun -s -- which bun 验证。
看到 "bun runtime extraction" 日志后再无智能体活动
嵌入式 bun 构建会在首次运行时把 bun 解压到数据目录。若解压失败 (通常是权限问题),智能体引擎就没有运行时。请检查数据目录中是否 存在 bun 二进制,确认服务用户拥有数据目录,并在日志里查看真实的 解压错误。
Office 预览
Word/Excel/PPT 预览返回 "LibreOffice not detected"
/api/star-office/detect 路由会在系统中探测 LibreOffice。Office 预览
功能(/api/word-preview/*、/api/excel-preview/*、
/api/ppt-preview/*、/api/document/convert)需要 LibreOffice 才能
渲染文档。
- Linux:
apt install libreoffice(或同等发行版命令)。 - macOS:
brew install --cask libreoffice。 - Windows:从 libreoffice.org 安装。
安装后请重启后端,让它重新探测。
预览 iframe 一直空白
Office 预览路由会派生 LibreOffice 子进程,并通过
/api/ppt-proxy/* 与 /api/office-watch-proxy/* 代理它们。这些代理
路由是有意公共(不鉴权)的——iframe 内容必须在不带 SPA 会话
cookie 的情况下加载。如果你的反向代理剥掉了 URL 路径段,或在边缘对
/api/* 全部加了鉴权,请把这些 proxy 路径豁免出去。
数据目录权限
服务起来了,但数据库写入失败 / "unable to open database file"
配置的数据目录必须对进程可写。常见情况:
- 在 systemd 下以
User=nomifun运行,但数据目录的所属者是另一个 用户。修复:chown -R nomifun:nomifun /var/lib/nomifun。 - 一个只读挂载(
RootDirectory=、ProtectHome=yes等)覆盖了数据 路径。请去掉过宽的沙箱;保留官方 unit 中的中度加固 (NoNewPrivileges=yes、PrivateTmp=yes)。 - 在 Docker 下,把宿主目录挂载进来但 UID 与容器不一致。请改用命名
卷,或将宿主目录
chown到正确的 UID。
桌面外壳的默认数据目录是按用户的应用数据目录(Windows 上是
%LOCALAPPDATA%\NomiFun\Nomi,macOS 上是
~/Library/Application Support/NomiFun/Nomi,Linux 上是
$XDG_DATA_HOME/NomiFun/Nomi),它天然对启动应用的用户可写。设置
NOMIFUN_DATA_DIR=<absolute path> 后目录会变成
$NOMIFUN_DATA_DIR/Nomi。位于 <system temp>/nomifun-data/Nomi 的
遗留安装会在启动时被自动搬迁到新默认位置(旧目录保留为备份);若
搬迁未能完成,应用会继续从遗留目录启动,并在下次启动时重试。
data directory ... is already in use by another running NomiFun backend(数据目录被占用)
所有宿主(桌面外壳、nomifun-web、nomicore 二进制)默认使用
同一个按用户的数据目录,而后端启动时会对 {data_dir}/server.lock
取一把 OS 级排他锁——同一目录上的第二个后端会以这条信息快速失败,
而不是悄悄破坏共享状态。最经典的触发方式:桌面应用还开着,又去跑
bun run serve:web / dev:web(或反过来)。两条出路:关掉另一个实例
(错误信息会写明持有者的 pid 与可执行文件名),或用
NOMIFUN_DATA_DIR / --data-dir 给新实例指一个独立目录。持有者
退出或崩溃时锁由 OS 自动释放;残留的 server.lock 文件无害。
nomicore doctor 与 mcp-* stdio 子命令不取这把锁,因此不受影响。
Docker 专项
docker compose up 完成构建并启动后立刻退出
请阅读日志(docker compose logs nomifun)。最常见的原因有:
- 数据卷为空而且没有设置
NOMIFUN_ADMIN_PASSWORD——服务运行 正常,但你必须先通过 HTTP 完成首次启动设置才能进得去。这其实不是 失败,而是一种状态。 - 镜像内
--dist路径指错。官方 Dockerfile 把ui/dist复制到/opt/nomifun/web,CMD也据此引用——只在你定制了 Dockerfile 时才会出问题。 - 一个被 bind 挂载的、容器用户无写权限的数据目录。
日志显示 nomifun-web: embedded backend + SPA on one port 但浏览器无法连接
请确认端口映射(docker compose ps)。默认 compose 文件发布
8787:8787;如果你在前面放了 Caddy,应改为 expose: ["8787"]。
连错端口是最常见的元凶。
在企业代理后构建很慢或失败
构建时传入 cargo 注册表镜像:
docker build --build-arg CARGO_REGISTRY_MIRROR=https://rsproxy.cn/index/ -t nomifun-web:local .
(或者你环境中使用的任意镜像。)
日志
后端同时写入 stdout 与 <log-dir>/nomicore.log 上的按日滚动文件
(默认 <data-dir>/logs)。出问题时:
journalctl -u nomifun-web/docker compose logs nomifun视图能 看到最近几分钟的日志。<log-dir>下的滚动文件保存了历史。- 把受影响模块的级别拉高:MCP 问题用
--log-level info,nomifun_mcp=trace,终端用info,nomifun_terminal=debug,智能体会话用info,nomifun_conversation=debug。
当一切都不奏效
读源码。每个路由 handler 都在其归属 crate 的 routes.rs(或
routes/)文件里;装配在
crates/backend/nomifun-app/src/router/routes.rs。handler 抛出的错误
信息就是 HTTP 响应里那串字面量字符串,所以一次精确 grep 通常几秒就
能落到出错的检查上。