Multica Docs

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.

Вероятные причины:

  1. MULTICA_SERVER_URL указывает не туда — default ws://localhost:8080/ws; self-host нужен ваш server address
  2. Network / firewall — daemon и server не в одной сети или исходящий traffic блокируется
  3. Token expired или invalid — не запускали multica login или PAT revoked
  4. Server отклонил registration — аккаунт входа не member target workspace (register → 403)
  5. 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.

Вероятные причины (по частоте):

  1. Лимит concurrency agentmax_concurrent_tasks agent (default 6) занят другими running tasks
  2. Другой task того же agent на том же issue ещё running — тот же agent × тот же issue выполняются последовательно (без duplicate execution)
  3. Agent archived — после archival новые tasks enqueue, но не claim; timeout через 5 минут (code-issue G-01)
  4. Daemon не зарегистрировал runtime в текущем workspace — restart daemon или reselect runtime в UI
  5. Daemon disconnected — нет heartbeat 45 секунд. daemon status online может отражать очень недавний 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 всё равно выполняются.

Вероятные причины:

  1. Origin check failure — frontend domain не в CORS allowlist server. Default только localhost:3000/5173/5174; public self-host нужен FRONTEND_ORIGIN
  2. Protocol mismatch — frontend на https:// нужен wss://; HTTP — ws://
  3. Reverse proxy без WebSocket upgrade — Nginx / Envoy / HAProxy по умолчанию не форвардят Upgrade
  4. 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/ws101 Switching ProtocolsUpgrade)

Исправление:

  • Wrong origin → FRONTEND_ORIGIN=https://multica.yourdomain.com в .env server (или comma-separated CORS_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_HOST non-empty побеждает Resend)
  • EmailService: Resend API from=<addr> — Resend
  • EmailService: 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

Вероятные причины:

  1. RESEND_API_KEY не задан — server молча fallback и пишет code в stdout без error. Легко в production
  2. Resend API key invalid / quota — logs "failed to send verification code"
  3. Domain RESEND_FROM_EMAIL не verified в Resend — Resend отказывает
  4. Письмо отправлено, но 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

Исправление:

Когда активен SMTP

SMTP path оборачивает каждый failure стадией — grep "failed to send verification email" / "failed to send invitation email":

Logged errorWhat it meansHow to fix
smtp dial <host>:<port>: dial tcp …: connect: connection refused / i/o timeoutBackend 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 другой authCredentials с mail admin; Exchange anonymous internal relay — оба empty
smtp MAIL FROM: 550 5.7.1 Client does not have permissions to send as this senderRelay не принимает RESEND_FROM_EMAIL как envelope sender — Exchange «anonymous users not allowed» или DMARCRESEND_FROM_EMAIL domain relay принимает; Exchange — ms-Exch-SMTP-Accept-Any-Sender на receive connector
smtp RCPT TO <addr>: 550 5.7.1 Unable to relayReceive connector не relay external recipients для вашей subnetInternal recipients only или subnet Multica в Exchange «Anonymous Users → Relay»
smtp DATA / smtp write body / smtp end dataSession OK, body dropped — size limits, filtering, reset mid-streamRelay 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 вроде 888888invalid or expired code.

Вероятные причины (взаимоисключающие):

  1. MULTICA_DEV_VERIFICATION_CODE empty — fixed codes disabled by default
  2. APP_ENV=productioncorrect для production; fixed local test codes ignored
  3. 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.

Исправление:

Usage dashboard остаётся на нуле

Симптом: agents завершают tasks, raw token usage в базе, но Settings → Usage и Settings → Runtime показывают 0 input / output / cost. Тихо — ошибок в backend logs нет.

Вероятные причины:

  1. rollup_task_usage_hourly() never claimed — dashboards читают derived task_usage_hourly, populated этой function. С MUL-2957 backend rollup in-process через DB scheduler (sys_cron_executions); stale build, missing migration 113, sustained outage без replicas — нет recent SUCCESS row
  2. pg_cron compatibility, wrong databasepg_cron.database_name default postgres; другое имя DB Multica — job не видит rollup_task_usage_hourly(). In-process scheduler не зависит; если только pg_cron — DB name must match
  3. 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 для optional cron.unschedule

Migration 103 fails with refusing to drop legacy daily rollups

Симптом: upgrade v0.3.4v0.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.

Исправление:

  1. 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
  2. Re-run upgrade — restart backend достаточно, migrations on startup. Guard видит current watermark, 103 applies.

  3. 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 startaddress already in use.

Вероятные причины:

  1. Server port занят (default 8080)
  2. Daemon health port занят (default 19514, offset by hash per profile)
  3. Web dev server conflict (3000 / 5173)
  4. 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

ComponentLocationCommand
Daemon~/.multica/daemon.log (background mode) or foreground stdoutmultica daemon logs -f --lines 100
Server (Docker)Container stdoutdocker logs -f <container>
Server (systemd)journaljournalctl -u multica-server -f
Frontend (dev)Terminal running pnpm devRead directly
Frontend (browser)DevTools → ConsolePress F12

Подробнее daemon logs — foreground: multica daemon stop && multica daemon start --foreground.