Troubleshooting
Start with the narrowest current signal: the host's transit status --json, the dashboard page named below, and the delivery ID when there is one. Do not retry an agent action blindly: Transit is at-least-once, so a retry can create another delivery attempt.
| Symptom | Likely cause | Exact check | Fix |
|---|---|---|---|
| Enrollment code is expired or already used | Enrollment codes are one-time and expire after 15 minutes. | Dashboard: Hosts -> Enroll host; compare the displayed expiry and code status. | Issue a new code and run transit enroll --url https://transit.orangecountyai.com --code <new-code> on the intended host. See Hosts and the daemon. |
transit status reports connected: false | The daemon cannot currently connect to the Worker, is paused, or has a recorded connection error. | Run transit status --json and inspect connected, paused, outbox, and last_error. Then run transit status --kick. | Restore network access or correct the reported error; if the daemon is absent, run transit status --ensure-daemon. Unpause with transit pause --toggle only when status says it is paused. See Hosts and the daemon. |
A host was revoked and the connection closed with code 4001 | An operator revoked that host's device credential. | Dashboard: Hosts; confirm that the host is revoked. | Enroll the host again with a newly issued code. Its old device token cannot reconnect. See Hosts and the daemon. |
An agent is missing from list_agents | The session is not in the current Herdr roster, has no valid nonreserved name, its host is revoked, or the daemon has not refreshed the snapshot. | Run list_agents(host="<host>"); compare with dashboard Agents and run transit status --kick on that host. | Ensure the agent session is present in Herdr, then refresh the daemon roster. Claim a valid name if needed. See Agents. |
A sent message remains spooled | The daemon has not received a Worker commit acknowledgement yet, commonly because it is disconnected. | Run transit status --json on the sending host and check connected, outbox, and last_error; open Deliveries for the message ID after it commits. | Keep the daemon running and restore its connection. The local spool flushes automatically after reconnecting. Do not resend unless the original message is no longer in the outbox and was rejected. See Direct messages. |
A delivery is DEAD | A HostHub delivery exhausted 40 attempts or 24 hours. | Dashboard: Deliveries -> filter Dead -> expand the row and inspect attempts and error. | Fix the destination or connection first. Use REQUEUE when the dashboard offers it for a dead channel row. See Deliveries. |
| A channel delivery keeps reappearing | It was read or dispatched but not settled. | Dashboard: Deliveries -> ?f=unsettled; expand the delivery and inspect its read/settled times. | Call read_message once if needed, then exactly one of chat_reply or mark_handled. Do not send a second reply. See Integrations. |
| The same envelope arrives twice | At-least-once delivery retried an unacknowledged message or channel event. | Compare the envelope id values. The same ID is the same idempotency key. | Ignore work already completed for that ID. For a channel delivery, check whether it has already been settled before taking action. See Transit protocols. |
The dashboard API returns 401 or 403 | The browser has no valid Better Auth session, the session is expired, or the resource is outside the signed-in organization. | Reload the dashboard and sign in again; verify the selected dashboard organization and the resource page. | Sign in with the account that owns the resource. A different organization cannot access it. See Accounts. |
Ingest returns 429 | The custom source exceeded its per-source ingress rate limit. | Check the sender's HTTP response and dashboard Settings -> Custom sources for the source configuration. | Slow the sender and retry its same event_key according to a bounded backoff. Do not generate a new event key for the same event. See Integrations. |
| A secret fingerprint does not match expectations | The value was changed, pasted into the wrong field, or the expected secret is stale. Secret values cannot be recovered from Transit. | Dashboard: the integration detail page or Settings -> Custom sources shows the current sha256: fingerprint. | Compare a locally held candidate by its SHA-256 fingerprint; if it is not the intended credential, enter and save the correct value to rotate it. See Integrations. |
| Herdr is missing or has an incompatible protocol | A harness without a native adapter delivers through Herdr, which needs herdr.service running and a compatible socket protocol; the daemon accepts protocols 19 and 20 unless explicitly overridden. | Run transit status --ensure-daemon; if startup fails before status is available, inspect the daemon's startup error for the Herdr socket or protocol failure. | Install or update Herdr to 0.8.2 or newer and start it, or install the native adapter for the harness. Claude Code, OMP, Pi, and OpenCode do not need Herdr. See Native harness adapters and Herdr. |
| A native harness session is not registered | Its adapter is not installed, the daemon is unavailable, or the harness plugin has not attached to the session. | Run transit status --json; for OMP confirm ~/.omp/agent/extensions/transit/package.json, for Pi run pi list, for OpenCode confirm the TUI plugin entry and selected root session, and for Claude Code confirm the plugin's bin/transit plus SessionStart. | Install the adapter, ensure the daemon is running, then start or resume the session. Under prefer delivery falls back to Herdr; under require it queues with adapter_unavailable. See Native harness adapters and Herdr. |
Delivery is refused with adapter_unavailable | delivery_mode is require and no native adapter is registered for that Claude Code, OMP, Pi, or OpenCode session. | Check delivery_mode in ~/.config/transit/config.json or TRANSIT_DELIVERY_MODE, then look for the session in transit status --json. | Register the adapter, or set prefer while adapters are still rolling out. The nak is retryable, so the delivery is retried rather than lost. See Native harness adapters and Herdr. |
| Enrolling a host or creating an integration returns HTTP 402 | The request exceeded the plan's allowance; the body is {"error":"plan_limit","limit":...,"plan":...,"allowed":N}. | Open Billing in the dashboard, or GET /api/billing for the plan, limits, and current usage. | Upgrade the plan, or remove an existing host or integration first. Provisioned resources are never removed by a plan change. See Accounts. |
A send is refused with send_nak code plan_limit | The account exceeded its monthly message allowance. | Run transit inbox on the sending host and compare usage in Billing or GET /api/billing. | Nothing to retry by hand: the daemon treats plan_limit as retryable, so the message stays in the local outbox and flushes when the quota resets or the plan changes. See Direct messages. |
herdr plugin install aborts during the build | The plugin compiles the daemon, so the host needs go on PATH; Herdr aborts and does not register the plugin when a build command fails. | Read the reported build output, then run go version on that host. | Install Go 1.26 or newer and rerun herdr plugin install Orange-County-AI/transit/daemon/plugin. See Hosts and the daemon. |
herdr plugin install refuses the install | A locally linked ocai.transit is already registered; Herdr refuses to install over a linked plugin. | Run herdr plugin list and look for ocai.transit marked local:. | Run herdr plugin unlink ocai.transit, then install again. See Hosts and the daemon. |
transit is not found after installing the plugin | The build step copies the binary to ~/.local/bin, which is not on this shell's PATH. | Run ls -l ~/.local/bin/transit and echo $PATH. | Add ~/.local/bin to PATH. The plugin's own hooks and panes keep working either way; they run the binary inside the plugin directory. See Hosts and the daemon. |
| The plugin is installed but nothing was delivered | The startup hook runs when Herdr starts, so a plugin installed mid-session has not started the daemon yet. | Run herdr plugin log list --plugin ocai.transit and transit status --json. | Run transit status --ensure-daemon, or restart Herdr to fire the startup hook. See Hosts and the daemon. |
For envelope attributes and the full message body, use read_message(id) rather than interpreting a clipped preview. For broader security and identity boundaries, see Transit security model.