Multica Docs

故障排查

排查连接、执行、实时更新、邮件和自托管服务中的常见问题。

先确认问题发生在哪一层: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 表示运行还在等待运行时领取。依次检查:

  1. 智能体绑定的运行时是否在线;
  2. 运行时是否检测到了该智能体配置的 AI 编程工具;
  3. 智能体是否还有可用并发额度;
  4. 守护进程是否还有全局执行容量。

智能体默认最多同时执行 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 把新二进制当作另一个应用,重新请求授权;在有人处理之前文件访问会一直阻塞,守护进程也无法感知有授权弹窗在等待。

恢复访问:

  1. 在这台 Mac 上找到等待中的"想要访问"弹窗,点击允许,卡住的运行会继续。
  2. 无人值守的 Mac 请给新二进制授予完全磁盘访问权限。用 realpath "$(command -v multica)" 打印路径,然后在系统设置 → 隐私与安全性 → 完全磁盘访问权限中删除旧的 multica 条目并添加该路径(在文件选择器中按 ⌘⇧G 粘贴路径)。
  3. 按启动守护进程时的方式重启它: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>/health

multica 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 login
GODEBUG=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 backenddocker 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 任务 搜索已有问题或提交新的任务。

接下来