Troubleshooting

The top 7 common issues when self-hosting Concierto. Symptoms, causes, how to diagnose, how to fix.

Look up issues by symptom. Each entry gives you symptom / likely causes / how to diagnose / how to fix. If your situation isn't listed, open an issue on GitHub.

Daemon can't connect to the server

Symptom: concierto daemon's status command shows offline or connection refused; the server logs show no /api/daemon/register or /api/daemon/heartbeat requests. For how the daemon mechanism works, see Daemon and runtimes.

Likely causes:

  1. CONCIERTO_SERVER_URL points at the wrong address, default is ws://localhost:8080/ws; self-host must change it to your server address
  2. Network / firewall blocking. The daemon and server aren't on the same network, or outbound traffic is blocked
  3. Token expired or invalid. You never ran concierto login, or the PAT was revoked
  4. Server rejected registration, the account you signed in with isn't in the target workspace (register returns 403)
  5. DNS resolution failure, the hostname doesn't resolve on the daemon machine

How to diagnose:

concierto daemon logs --lines 100    # look for daemon-side errors
echo $CONCIERTO_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 ~/.concierto/config.json        # verify api_token exists
concierto workspace list            # confirm you're a member of the target workspace

How to fix: address each cause above. The two most common fixes are changing CONCIERTO_SERVER_URL and restarting the daemon (concierto daemon restart) and signing in again (concierto logout && concierto login).

Tasks stuck in queued

Symptom: after assigning an issue to an agent, the issue status flips to in_progress immediately, but a long time passes with no sign of agent execution on the page; concierto daemon status shows the daemon online.

Likely causes (ordered by frequency):

  1. Agent concurrency limit reached, this agent's max_concurrent_tasks (default 6) is fully occupied by other running tasks
  2. Another task from the same agent is still running on the same issue, same agent × same issue is forced to run sequentially (prevents duplicate execution)
  3. Agent has been archived. After archival, new tasks still enqueue but can't be claimed, and they time out after 5 minutes (code-issue G-01)
  4. Daemon hasn't registered this runtime in the current workspace, restart the daemon or reselect the runtime in the UI
  5. Daemon disconnected, no heartbeat in the last 45 seconds. daemon status reporting online may reflect a very recent disconnect

How to diagnose:

concierto daemon status --output json       # runtime list + last_seen_at
concierto agent list                         # check agent archived state
concierto issue show <issue-id>             # inspect task history

On the server side (self-host), grep for "no_tasks" / "no_capacity" to see the claim outcome.

How to fix:

  • Concurrency full → wait for running tasks to finish, or concierto agent update <id> --max-concurrent-tasks 10 to raise the ceiling
  • Same-issue serialization → wait for the previous task to finish, or reassign to a different agent
  • Agent archived → concierto agent restore <id>
  • Runtime not registered → concierto daemon restart, and the daemon will re-register

WebSocket can't connect

Symptom: the browser console logs WebSocket is closed; the page doesn't show real-time updates (task progress, comments, inbox), and a refresh is needed to see them; backend tasks still execute.

Likely causes:

  1. Origin check failure, your frontend domain isn't in the server's CORS allowlist. The default allowlist only includes localhost:3000/5173/5174; self-hosting on the public internet requires FRONTEND_ORIGIN
  2. Protocol mismatch, frontend on https:// needs wss://; HTTP uses ws://
  3. Reverse proxy doesn't enable WebSocket upgrade, Nginx / Envoy / HAProxy don't forward the Upgrade header by default
  4. JWT cookie expired or missing, no re-sign-in after the 30-day expiry

How to diagnose:

  • Browser DevTools → Network → filter by "WS" and check connection state and status code
  • Grep server logs for "rejected origin" / "websocket", an origin issue spells itself out
  • curl -i http://<server-host>:8080/ws should return 101 Switching Protocols (with the Upgrade header)

How to fix:

  • Wrong origin → set FRONTEND_ORIGIN=https://nstack.yourdomain.com in the server's .env (or comma-separated CORS_ALLOWED_ORIGINS) and restart the server
  • Protocol mismatch → make sure FRONTEND_ORIGIN's protocol matches the frontend's
  • Reverse proxy → in Nginx, add proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
  • Cookie expired → refresh the page and sign in again

Emails not received

Symptom: after submitting an email during sign-in or invite acceptance, neither the inbox nor the spam folder has the verification code.

Likely causes:

  1. RESEND_API_KEY not set, the server silently falls back and writes the code to its own stdout without error. Easy to trip over in production
  2. Resend API key invalid / out of quota, server logs show "failed to send verification code"
  3. RESEND_FROM_EMAIL's domain not verified in Resend, Resend refuses to send
  4. Email was sent but flagged as spam by the recipient's ISP, check the Resend dashboard and the spam folder

How to diagnose:

  • Grep server logs for "[DEV] Verification code for". If present, Resend isn't configured and the code was written to stdout
  • Resend dashboard → Emails for send history
  • Confirm RESEND_FROM_EMAIL's domain appears in the Resend console's "Verified Domains" list

How to fix:

  • Missing API key → follow Sign-in and signup configuration → How email works to configure and restart the server
  • Domain not verified → run the DNS verification flow in the Resend console (add SPF / DKIM records)
  • In an emergency (internal testing) → copy the code printed under [DEV] from the server logs

Fixed local test code doesn't work

Symptom: on a self-hosted instance, you try to sign in with a fixed local test code such as 888888 and it's rejected with invalid or expired code.

Likely causes (mutually exclusive):

  1. CONCIERTO_DEV_VERIFICATION_CODE is empty, fixed codes are disabled by default
  2. APP_ENV=production, this is the correct production configuration; fixed local test codes are ignored in production
  3. The configured code is not 6 digits, the shortcut only accepts a 6-digit value

How to diagnose:

cat .env | grep -E 'APP_ENV|CONCIERTO_DEV_VERIFICATION_CODE'
docker exec <container> env | grep -E 'APP_ENV|CONCIERTO_DEV_VERIFICATION_CODE'

Check your inbox (including spam) for the real verification code.

How to fix:

  • In production, leave CONCIERTO_DEV_VERIFICATION_CODE empty, configure Resend and use real codes
  • For local development or internal testing, either copy the generated code from server logs or set APP_ENV=development plus CONCIERTO_DEV_VERIFICATION_CODE=888888, never enable a fixed code on a public instance (see Sign-in and signup configuration → Fixed local testing codes)

Port conflicts

Symptom: concierto server or concierto daemon start fails with address already in use.

Likely causes:

  1. Server port taken (default 8080)
  2. Daemon health port taken (default 19514, offset by a hash per profile)
  3. Web dev server port conflict (3000 / 5173)
  4. Insufficient privileges for the port (binding a privileged port < 1024 requires sudo)

How to diagnose:

lsof -i :8080        # macOS / Linux
netstat -ano | findstr :8080    # Windows

How to fix:

  • Kill the conflicting process (kill -9 <PID>), or change ports via PORT=9000
  • To use 80 / 443 → don't bind directly; put a reverse proxy (Nginx / Caddy) in front, forwarding to a high port

Where to find logs

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

For more detailed daemon logs, move it from background to foreground: concierto daemon stop && concierto daemon start --foreground.