Troubleshooting
Типичные проблемы при self-host Multica — симптомы, причины, диагностика, исправление.
Ищите по симптому. Каждая запись: симптом / вероятные причины / диагностика / исправление. Если вашего случая нет — откройте issue на GitHub.
Daemon не подключается к server
Симптом: multica daemon status показывает offline или connection refused; в логах server нет /api/daemon/register или /api/daemon/heartbeat. Механика daemon — Daemon and runtimes.
Вероятные причины:
MULTICA_SERVER_URLуказывает не туда — defaultws://localhost:8080/ws; self-host нужен ваш server address- Network / firewall — daemon и server не в одной сети или исходящий traffic блокируется
- Token expired или invalid — не запускали
multica loginили PAT revoked - Server отклонил registration — аккаунт входа не member target workspace (register → 403)
- DNS failure — hostname не резолвится на machine daemon
Диагностика:
multica daemon logs --lines 100 # look for daemon-side errors
echo $MULTICA_SERVER_URL # confirm the address is set
curl -i http://<server-host>:8080/health # hit the server directly
curl -i http://<server-host>:8080/readyz # include DB + migration readiness
cat ~/.multica/config.json # verify api_token exists
multica workspace list # confirm you're a member of the target workspaceИсправление: по каждой причине выше. Чаще всего — сменить MULTICA_SERVER_URL и restart daemon (multica daemon restart) и sign in снова (multica logout && multica login).
Task застряли в queued
Симптом: после assign issue agent статус issue сразу in_progress, но долго нет признаков выполнения agent на странице; multica daemon status показывает daemon online.
Вероятные причины (по частоте):
- Лимит concurrency agent —
max_concurrent_tasksagent (default 6) занят другими running tasks - Другой task того же agent на том же issue ещё running — тот же agent × тот же issue выполняются последовательно (без duplicate execution)
- Agent archived — после archival новые tasks enqueue, но не claim; timeout через 5 минут (code-issue G-01)
- Daemon не зарегистрировал runtime в текущем workspace — restart daemon или reselect runtime в UI
- Daemon disconnected — нет heartbeat 45 секунд.
daemon statusonlineможет отражать очень недавний disconnect
Диагностика:
multica daemon status --output json # runtime list + last_seen_at
multica agent list # check agent archived state
multica issue show <issue-id> # inspect task historyНа server (self-host) grep "no_tasks" / "no_capacity" для claim outcome.
Исправление:
- Concurrency full → дождитесь finish running tasks или
multica agent update <id> --max-concurrent-tasks 10 - Same-issue serialization → дождитесь previous task или reassign другому agent
- Agent archived →
multica agent restore <id> - Runtime not registered →
multica daemon restart, daemon re-register
WebSocket не подключается
Симптом: browser console WebSocket is closed; страница без realtime updates (task progress, comments, inbox), нужен refresh; backend tasks всё равно выполняются.
Вероятные причины:
- Origin check failure — frontend domain не в CORS allowlist server. Default только
localhost:3000/5173/5174; public self-host нуженFRONTEND_ORIGIN - Protocol mismatch — frontend на
https://нуженwss://; HTTP —ws:// - Reverse proxy без WebSocket upgrade — Nginx / Envoy / HAProxy по умолчанию не форвардят
Upgrade - JWT cookie expired или missing — нет re-sign-in после 30-day expiry
Диагностика:
- Browser DevTools → Network → filter «WS», state и status code
- Grep server logs
"rejected origin"/"websocket"— origin issue виден явно curl -i http://<server-host>:8080/ws→101 Switching Protocols(сUpgrade)
Исправление:
- Wrong origin →
FRONTEND_ORIGIN=https://multica.yourdomain.comв.envserver (или comma-separatedCORS_ALLOWED_ORIGINS), restart - Protocol mismatch → protocol
FRONTEND_ORIGINсовпадает с frontend - Reverse proxy → Nginx:
proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; - Cookie expired → refresh и sign in снова
Email не приходит
Симптом: после email при sign-in или accept invite ни inbox, ни spam не содержат verification code.
Сначала — какой provider активен у server. При старте backend одна из строк:
EmailService: SMTP relay <host>:<port> from=<addr>— SMTP (SMTP_HOSTnon-empty побеждает Resend)EmailService: Resend API from=<addr>— ResendEmailService: DEV mode — codes printed to stdout …— provider не настроен
docker compose -f docker-compose.selfhost.yml logs backend | grep "EmailService:"Если ожидаемой строки нет — env не дошёл до process; .env и docker compose -f docker-compose.selfhost.yml exec backend env | grep -E 'RESEND_|SMTP_'. Credentials на этой startup line не логируются.
Когда активен Resend
Вероятные причины:
RESEND_API_KEYне задан — server молча fallback и пишет code в stdout без error. Легко в production- Resend API key invalid / quota — logs
"failed to send verification code" - Domain
RESEND_FROM_EMAILне verified в Resend — Resend отказывает - Письмо отправлено, но spam у ISP — Resend dashboard и spam folder
Диагностика:
- Grep
"[DEV] Verification code for"— Resend не configured, code в stdout - Resend dashboard → Emails
- Domain
RESEND_FROM_EMAILв «Verified Domains» Resend console
Исправление:
- Missing API key → Sign-in and signup configuration → How email works, restart
- Domain not verified → DNS verification в Resend (SPF / DKIM)
- Emergency internal test → code под
[DEV]из server logs
Когда активен SMTP
SMTP path оборачивает каждый failure стадией — grep "failed to send verification email" / "failed to send invitation email":
| Logged error | What it means | How to fix |
|---|---|---|
smtp dial <host>:<port>: dial tcp …: connect: connection refused / i/o timeout | Backend container не достигает relay — wrong host, port, firewall, relay не слушает | SMTP_HOST / SMTP_PORT из container (docker compose -f docker-compose.selfhost.yml exec backend nslookup <host> и nc -vz <host> <port>); firewall host Multica → relay |
smtp starttls: x509: certificate signed by unknown authority (or certificate is not valid for any names) | Private CA / self-signed, trust store container отклоняет | CA в container или SMTP_TLS_INSECURE=true на trusted segment |
smtp auth: 535 5.7.8 Authentication credentials invalid (or 534/530) | SMTP_USERNAME / SMTP_PASSWORD неверны или relay другой auth | Credentials с mail admin; Exchange anonymous internal relay — оба empty |
smtp MAIL FROM: 550 5.7.1 Client does not have permissions to send as this sender | Relay не принимает RESEND_FROM_EMAIL как envelope sender — Exchange «anonymous users not allowed» или DMARC | RESEND_FROM_EMAIL domain relay принимает; Exchange — ms-Exch-SMTP-Accept-Any-Sender на receive connector |
smtp RCPT TO <addr>: 550 5.7.1 Unable to relay | Receive connector не relay external recipients для вашей subnet | Internal recipients only или subnet Multica в Exchange «Anonymous Users → Relay» |
smtp DATA / smtp write body / smtp end data | Session OK, body dropped — size limits, filtering, reset mid-stream | Relay logs по Message-ID (<unixnano>@<host>); raise size limit |
MAIL FROM, RCPT TO, DATA errors логируются с response code relay. Verification codes и invite tokens never в wrapped error.
Диагностика:
- Grep
"EmailService: SMTP relay"at startup,"failed to send"runtime - Из backend container:
docker compose -f docker-compose.selfhost.yml exec backend sh -c 'nc -vz $SMTP_HOST $SMTP_PORT' - Env в process:
docker compose -f docker-compose.selfhost.yml exec backend env | grep SMTP_(password в output — только trusted shell)
Исправление:
- Wrong host / port →
SMTP_HOST/SMTP_PORT, restart; modes — Auth setup → Option B: SMTP relay - Cert mismatch → CA в container или временно
SMTP_TLS_INSECURE=true - Auth failure → credentials; anonymous relay — empty username/password
Unable to relay→ internal only или relay permission на Exchange connector
Fixed local test code не работает
Симптом: на self-hosted пробуете sign in с fixed local test code вроде 888888 — invalid or expired code.
Вероятные причины (взаимоисключающие):
MULTICA_DEV_VERIFICATION_CODEempty — fixed codes disabled by defaultAPP_ENV=production— correct для production; fixed local test codes ignored- Configured code не 6 digits — shortcut только 6-digit
Диагностика:
cat .env | grep -E 'APP_ENV|MULTICA_DEV_VERIFICATION_CODE'
docker exec <container> env | grep -E 'APP_ENV|MULTICA_DEV_VERIFICATION_CODE'Проверьте inbox (spam) на real verification code.
Исправление:
- Production — пустой
MULTICA_DEV_VERIFICATION_CODE, Resend и real codes - Local / internal — code из logs или
APP_ENV=development+MULTICA_DEV_VERIFICATION_CODE=888888; never fixed code на public instance (Sign-in and signup configuration → Fixed local testing codes)
Usage dashboard остаётся на нуле
Симптом: agents завершают tasks, raw token usage в базе, но Settings → Usage и Settings → Runtime показывают 0 input / output / cost. Тихо — ошибок в backend logs нет.
Вероятные причины:
rollup_task_usage_hourly()never claimed — dashboards читают derivedtask_usage_hourly, populated этой function. С MUL-2957 backend rollup in-process через DB scheduler (sys_cron_executions); stale build, missing migration113, sustained outage без replicas — нет recent SUCCESS rowpg_croncompatibility, wrong database —pg_cron.database_namedefaultpostgres; другое имя DB Multica — job не видитrollup_task_usage_hourly(). In-process scheduler не зависит; если толькоpg_cron— DB name must match- Handler claimed но erroring — SQL function missing (partial migrations), DB role / search_path. FAILED rows в
sys_cron_executions
Диагностика:
-- Confirm raw events exist but the hourly table is empty.
SELECT count(*) AS raw_rows FROM task_usage;
SELECT count(*) AS hourly_rows FROM task_usage_hourly;
-- Inspect the in-process scheduler's audit log.
SELECT plan_time, status, attempt, runner_id,
error_code, error_msg, started_at, finished_at
FROM sys_cron_executions
WHERE job_name = 'rollup_task_usage_hourly'
ORDER BY plan_time DESC
LIMIT 20;
-- Watermark — if this is 1970-01-01, the rollup has never run.
SELECT watermark_at FROM task_usage_hourly_rollup_state;
-- Compatibility path: if you previously registered pg_cron, confirm
-- it is (or isn't) available and pointing at the right database.
SELECT * FROM pg_available_extensions WHERE name = 'pg_cron';
SHOW shared_preload_libraries;
SELECT jobname, schedule, database, active FROM cron.job;Исправление:
- Scheduler на ≥1 backend replica — каждые 30s SUCCESS row для
rollup_task_usage_hourlyвsys_cron_executions - Hand rollup:
SELECT rollup_task_usage_hourly();— refresh dashboard; если цифры появились — SQL OK, проблема в scheduler claim path - Migration
113_sys_cron_executionsне applied — restart backend илиmigrate up - Legacy
pg_cron— advisory lock 4246, double-write невозможен; Self-host quickstart → Usage rollup для optionalcron.unschedule
Migration 103 fails with refusing to drop legacy daily rollups
Симптом: upgrade v0.3.4 → v0.3.5+, backend не стартует (или migrate up abort):
ERROR: refusing to drop legacy daily rollups:
task_usage_hourly_rollup_state.watermark_at (1970-01-01 ...) trails
task_usage latest event (...) by more than 01:00:00 — backfill is
incomplete or pg_cron is not running. Run cmd/backfill_task_usage_hourly
(and let pg_cron catch up) before re-running migrateВероятная причина: fail-closed guard migration 103. Не drop legacy daily rollups, пока task_usage_hourly не догнал raw task_usage. Guard при existing rows и watermark на epoch.
С MUL-2957 migrate idempotent monthly-slice backfill (advisory lock 4246) автоматически перед migration 103 — v0.3.4 → v0.3.5+ one migrate up. Если ошибка остаётся — pre-MUL-2957 binary или hook failed; migrate logs строка task_usage hourly rollup hook.
Исправление:
-
Pre-MUL-2957 binary без upgrade binary first — standalone backfill (idempotent, safe interrupt/re-run):
# Docker Compose docker compose -f docker-compose.selfhost.yml exec backend \ ./backfill_task_usage_hourly --sleep-between-slices=2s # Kubernetes kubectl -n multica exec deploy/multica-backend -- \ ./backfill_task_usage_hourly --sleep-between-slices=2s -
Re-run upgrade — restart backend достаточно, migrations on startup. Guard видит current watermark,
103applies. -
In-process scheduler держит watermark — Self-host quickstart → Usage rollup.
--sleep-between-slices=2s — polite default для years of history. --months-back N --force-partial — только last N months, abandon older buckets.
Port conflicts
Симптом: multica server или multica daemon start — address already in use.
Вероятные причины:
- Server port занят (default
8080) - Daemon health port занят (default
19514, offset by hash per profile) - Web dev server conflict (
3000/5173) - Insufficient privileges (privileged port
< 1024нужен sudo)
Диагностика:
lsof -i :8080 # macOS / Linux
netstat -ano | findstr :8080 # WindowsИсправление:
- Kill conflicting process (
kill -9 <PID>) илиPORT=9000 - 80 / 443 — reverse proxy (Nginx / Caddy) на high port, не bind напрямую
Где искать logs
| Component | Location | Command |
|---|---|---|
| Daemon | ~/.multica/daemon.log (background mode) or foreground stdout | multica daemon logs -f --lines 100 |
| Server (Docker) | Container stdout | docker logs -f <container> |
| Server (systemd) | journal | journalctl -u multica-server -f |
| Frontend (dev) | Terminal running pnpm dev | Read directly |
| Frontend (browser) | DevTools → Console | Press F12 |
Подробнее daemon logs — foreground: multica daemon stop && multica daemon start --foreground.