故障排查
排查连接、执行、实时更新、邮件和自托管服务中的常见问题。
先确认问题发生在哪一层:Multica 服务、守护进程、运行时,还是 AI 编程工具。下面几条命令通常足以找到第一条有效错误:
multica version
multica auth status
multica daemon status --output json
multica daemon logs --lines 100自托管实例还可以直接检查服务:
curl -i https://api.example.com/health
curl -i https://api.example.com/readyz/health 只说明 API 进程正在响应;/readyz 还会检查数据库和 migration。问题反馈包含报错、相关日志、CLI 版本和操作系统;令牌、邮箱等敏感信息在提交前移除。
守护进程连接失败
先运行:
multica auth status
multica daemon status --output json
multica daemon logs --lines 100常见原因包括:
- CLI 尚未登录,或本机保存的令牌已经失效;
- 守护进程连接了错误的 Multica 服务;
- 执行电脑无法访问 API,或 DNS、TLS、防火墙拦截了连接;
- 当前账号已经不在目标工作区中;
- 本机没有安装任何受支持的 AI 编程工具,守护进程因此无法启动。
重新登录并重启守护进程:
multica login
multica daemon restart自托管部署还需要从执行电脑请求 API 的 /health——服务器本机测试无法暴露执行电脑侧的 DNS、TLS 或防火墙问题。需要修改地址时,重新运行 multica setup self-host,或检查当前 profile 的 server_url:
multica config show任务未开始执行
打开任务的执行日志,先看运行的当前状态和等待原因。
queued 状态
queued 表示运行还在等待运行时领取。依次检查:
- 智能体绑定的运行时是否在线;
- 运行时是否检测到了该智能体配置的 AI 编程工具;
- 智能体是否还有可用并发额度;
- 守护进程是否还有全局执行容量。
智能体默认最多同时执行 6 次运行;单个守护进程默认最多同时执行 20 次。达到上限时,新运行会留在队列中,等已有执行结束后再开始。运行时离线时,运行也会继续排队;只有该运行时心跳缺席超过重连宽限期、且该运行自己也已等满这么久时才会失败;所以排在忙碌运行时后面的长队列不会因为等太久而被判失败,指派给已经离线的机器时也仍然留有完整一个宽限期把它唤醒。
multica daemon status --output json
multica agent get <agent-id>
multica issue runs <issue-id>如果运行时列表缺少预期工具,先在同一个系统账号和 PATH 中确认工具可以运行并且已经登录,再执行 multica daemon restart。
waiting_local_directory 状态
这表示另一条在途运行正在使用同一个本地目录。Multica 会等待目录锁释放,避免两个智能体同时修改同一份文件。
这种等待只存在于目录的 in_place(“原地”)模式。如果这个目录是 git 仓库,把资源切到 worktree(“并行”)就没有队列了:每次运行拿到自己的 worktree,并以分支形式交回结果,谁都不用等谁。见项目资源。
否则通常只需等前一次运行结束。若前一次已经卡住,可以从它的执行日志中停止;也可以为当前智能体选择其他本地目录。这个目录互斥由守护进程在内存中维护,不写磁盘锁文件;怀疑锁状态异常时运行 multica daemon restart 即可释放,没有需要手动删除的文件。
AI 编程工具启动失败
守护进程在线不代表工具本身可用。打开这次执行的详细记录,重点检查:
- 工具是否已经完成登录;
- API key、额度或模型权限是否可用;
- 智能体选择的模型和思考级别是否被该工具支持;
- 本地工作目录是否存在并且可写;
- 智能体的自定义参数或环境变量是否有效。
先在执行电脑的终端中直接运行同一款工具。工具自身也无法开始时,先修复工具登录或配置,再从执行日志重试运行。
升级 CLI 后 macOS 上的运行卡住
通过 Homebrew 安装的 multica CLI 升级后(无论是 brew upgrade、multica update、运行时页面上的更新,还是守护进程自动更新),读取 ~/Desktop、~/Documents、~/Downloads、iCloud 云盘或其他受保护文件夹的运行可能停滞:智能体进程不占用 CPU,运行既不结束也不失败。
这种情况出现在 macOS 把隐私权限(TCC)记在守护进程本身名下时,由 launchd 启动的守护进程(例如 LaunchAgent)就是如此:等待中的弹窗显示的是 multica,智能体启动的所有工具都沿用守护进程的授权。Homebrew 版 CLI 没有使用固定身份签名,macOS 只能把授权绑定到这一次构建及其解析后的路径,例如 /opt/homebrew/Cellar/multica/<version>/bin/multica。升级后,macOS 把新二进制当作另一个应用,重新请求授权;在有人处理之前文件访问会一直阻塞,守护进程也无法感知有授权弹窗在等待。
恢复访问:
- 在这台 Mac 上找到等待中的"想要访问"弹窗,点击允许,卡住的运行会继续。
- 无人值守的 Mac 请给新二进制授予完全磁盘访问权限。用
realpath "$(command -v multica)"打印路径,然后在系统设置 → 隐私与安全性 → 完全磁盘访问权限中删除旧的multica条目并添加该路径(在文件选择器中按 ⌘⇧G 粘贴路径)。 - 按启动守护进程时的方式重启它:
multica daemon restart;LaunchAgent 则用launchctl kickstart -k gui/$(id -u)/<label>。
每次升级 CLI 后都需要重复这些步骤。只有连接 Multica Cloud 的守护进程默认开启自动更新;用 --no-auto-update 启动守护进程后,只在你手动升级时更新。桌面应用内置的 CLI 使用 Developer ID 签名,授权在升级后依然有效。
实时更新失效
运行仍能执行、但评论和状态不能实时出现,通常是 WebSocket 没有连通。
在浏览器开发者工具的 Network → WS 中查看 /ws 连接。自托管部署重点检查:
FRONTEND_ORIGIN是否和浏览器实际打开的地址一致;- HTTPS 页面是否通过
wss://连接; - 反向代理是否转发 WebSocket 的 Upgrade 请求;
- 浏览器登录是否已经过期。
直接用 curl 访问 /ws 返回 HTTP 400 是正常现象:该端点要求带 workspace 相关的查询参数,裸握手在进入 WebSocket 升级之前就会被拒。所以 backend 日志里出现 400/401 记录,只能说明普通 HTTP 路由已经到达 backend——并不能证明代理保留了 WebSocket Upgrade 头。要验证真正的连接,请打开浏览器开发者工具的 Network → WS 面板(或用支持 WebSocket 的客户端),看是否出现 101 握手。
自部署注意:daemon 的长连接拨的是 /api/daemon/ws(不是 /ws),同样要直通后端;握手失败时 daemon 会静默回退到轮询,backend 日志里该路径反复出现 status=400 即中招。
容器只在创建时读取 .env,修改后需要重新创建:
docker compose -f docker-compose.selfhost.yml up -d完整的反向代理示例见自托管快速上手。
multica setup 报告服务器不可达
CLI 的可达性探测会 GET <server-url>/health 并要求 200。backend 提供这个路径。新版 web 前端会把 /health 转发给 backend,旧版不会——把所有流量都转给旧版 frontend 的反向代理会返回 404,于是 CLI 误判服务不可用,而服务实际是健康的。
在代理里把 /health 显式转给 backend(8080 端口),对所有版本都适用——自托管快速上手里的两个 Caddy 示例都包含这条。确认方法:
curl -fsS <server-url>/healthmultica login 报告 TLS 握手超时
multica login 能打开浏览器、网页登录也能完成,但 CLI 仍以 "Sign-in did not complete" 或 "TLS 握手超时" 失败。multica --debug login 在 POST /api/tokens 上显示 net/http: TLS handshake timeout,而同一台机器上的 curl.exe -I https://api.multica.ai 或浏览器都能正常连接。
TCP 连接已建立,但 TLS 握手始终没有完成。Go 客户端(包括 Multica CLI 和守护进程)默认在 TLS ClientHello 中携带后量子密钥交换,报文约 1.5 KB,会拆成两个包发送。部分安全软件、VPN、路由器和防火墙会丢弃这种握手;curl 的 ClientHello 小得多,所以能通过。
先关闭后量子密钥交换重试,确认是否为此原因:
$env:GODEBUG = 'tlsmlkem=0'
multica --debug loginGODEBUG=tlsmlkem=0 multica --debug login如果这样能成功,请为运行 CLI 和守护进程的用户永久设置 GODEBUG=tlsmlkem=0——Windows 上执行 [Environment]::SetEnvironmentVariable('GODEBUG', 'tlsmlkem=0', 'User') 后重新打开终端——然后重启守护进程或桌面应用。调高 MULTICA_HTTP_TIMEOUT 无效,它不控制握手。根本解决需要在网络侧处理:升级或重新配置丢弃分包 TLS 握手的设备或软件。
验证码与邀请邮件未送达
先查看 backend 启动日志。日志会说明当前使用 SMTP relay、Resend API 还是 DEV mode:
docker compose -f docker-compose.selfhost.yml logs backend \
| grep "EmailService:"- DEV mode:邮件不会发出,验证码和邀请链接只写入 backend 日志;
- Resend:确认 API key 有效,且发件地址的域名已经验证;
- SMTP:确认主机、端口、凭据和发件地址,并从错误日志判断失败发生在连接、TLS、认证还是投递阶段。
SMTP_HOST 和 Resend 同时配置时,Multica 会优先使用 SMTP。配置方式见登录与注册。
生产环境不要依赖日志中的验证码,也不要启用固定本地测试验证码。
附件上传或下载失败
先检查 backend 日志和响应状态码。常见原因是:
- 反向代理限制了请求体大小;
- 本地上传目录不可写或没有挂载持久化卷;
- S3 bucket、region、endpoint 或凭据不匹配;
- 下载地址经过代理后使用了错误的公开域名或协议。
使用 Docker Compose 时,默认的 backend_uploads volume 保存本地附件。重建容器不会删除它,但 docker compose down -v 会删除数据卷。S3 相关配置见环境变量。
用量(Usage)为 0
Usage 页面读取按小时汇总的数据,不直接读取每次运行的原始用量。先确认原始数据和汇总表:
SELECT count(*) FROM task_usage;
SELECT count(*) FROM task_usage_hourly;
SELECT plan_time, status, error_code, error_msg
FROM sys_cron_executions
WHERE job_name = 'rollup_task_usage_hourly'
ORDER BY plan_time DESC
LIMIT 20;如果 task_usage 有数据、汇总表为空,并且调度记录显示失败,先确认 migration 已经全部应用;升级被 migration 103 拒绝执行的情况见下一节。也可以手动执行一次汇总来区分 SQL 和调度问题:
SELECT rollup_task_usage_hourly();手动执行后数字正常,说明汇总函数可用,问题在 backend 的定时调度;手动 SQL 只补一次汇总,不会恢复调度。按小时汇总由 backend 内置调度执行,不需要自行配置 pg_cron。
升级时 migration 103 拒绝执行
正常升级不需要为 103 做任何事:migrate up 在应用它之前会自动回填历史用量数据——空库直接通过,有历史数据的实例自动按月补齐后继续。
如果 backend 启动仍报 refusing to drop legacy daily rollups,说明自动回填没有完成(例如回填中途失败,或没有通过 migrate up 而是直接应用了 SQL)。此时手动运行回填命令,完成后重启 backend:
cd server
DATABASE_URL='postgres://...' go run ./cmd/backfill_task_usage_hourly常用 flag:--dry-run 只预览不写入;--sleep-between-slices 在切片之间加间隔,降低对繁忙实例的读压。命令按月切片、幂等,中断后可以直接重跑;它持有 advisory lock,与服务端的定时汇总互斥,不会产生重复或不一致的汇总数据。完成后重启 backend,用 /readyz 确认 migrations 为 ok。
端口占用
本地常用端口包括 API 的 8080、Web 的 3000 和守护进程健康检查端口。先找出占用者:
lsof -nP -iTCP:8080 -sTCP:LISTEN # macOS / Linux
netstat -ano | findstr :8080 # Windows如果它是另一个 Multica checkout,先在那个目录运行 make stop。否则正常停止占用端口的程序,或修改当前服务端口。公网 80/443 由 Caddy、Nginx 等反向代理监听。
日志位置
| 组件 | 查看方式 |
|---|---|
| 后台守护进程 | multica daemon logs --lines 100 |
| 实时跟随守护进程日志 | multica daemon logs --follow |
| 默认 profile 的日志文件 | ~/.multica/daemon.log |
| 默认 profile 的启动或崩溃日志 | ~/.multica/daemon.err.log |
| 命名 profile | ~/.multica/profiles/<name>/ 下的对应日志 |
| Docker backend | docker compose -f docker-compose.selfhost.yml logs -f backend |
| 浏览器 | 开发者工具中的 Console 和 Network |
哪个文件是当前活跃的日志,取决于守护进程启动时使用的 profile;早前守护进程残留的旧日志同样能正常读出来,最容易让人调错文件。不要靠猜去开文件:multica daemon logs 会在输出日志内容前先打印它解析到的绝对路径。读命名 profile 的日志时加上 --profile <name>。
需要直接观察守护进程启动过程时,可以改为前台运行:
multica daemon stop
multica daemon start --foreground仍然无法定位时,到 GitHub 任务 搜索已有问题或提交新的任务。