Troubleshooting

Start every investigation the same way:

cohort doctor

Doctor is the end-to-end gate. It exits non-zero when something is wrong and prints a remedy per failure — most commonly "run the upgrade".

What doctor checks

Installation. Framework files (config/agent.json and its generated wrapper, CLAUDE.md, .claude/settings.json, the daemon and setup scripts), the cadence-bus tree (state/cadence-bus/{inbox,claimed,processed,failed,dlq}), and dependencies (node_modules, the Claude CLI, jq, the emergency-stop script).

Plist architecture. Every generated plist must carry the cadence-bus marker; any plist still invoking the legacy spawn-per-tick pattern is flagged, and doctor prints the exact launchctl unload && launchctl load commands to migrate installed jobs.

Liveness. state/cadence-bus/health.json must be fresh (under 60 s) when the daemon runs, and doctor enqueues a heartbeat tick to prove the producer side end to end.

Environment. .env present with ANTHROPIC_API_KEY validated live against the API — an invalid key here cascades 401s through every spawn.

Cohort connectivity (once enrolled; an un-enrolled agent gets a single "not enrolled" line and doctor stays useful offline):

  1. Config resolution — env plus config/org.yaml.
  2. Directory reachability with latency, and the server's protocol version against the SDK's.
  3. The pairing probe — a messaging.channels call that fails with FORBIDDEN_SCOPE when the key is not paired to a workforce member.
  4. The workspace mailbox — one inbox probe whenever config/orgmail.yaml exists.

Failures and fixes

Install and setup

| Symptom | Cause and fix | | --- | --- | | Not a Maestro agent directory (no config/agent.json here) | You are outside an agent repo. cd into one, or create it: npx @cohortapp/agent-sdk create <dirname> | | Setup ends with "Some company-specific slots are still skeletons" | You ran with --no-enrich or enrichment partially failed. Re-run maestro setup — it resumes from the checkpoint and only fills what is pending | | Setup exits non-zero | A hard failure: missing identity, unresolvable archetype, or the Claude API unreachable. Fix the reported item and re-run; everything else (optional channels, enrichment) is non-fatal by design |

Workspace connectivity

| Symptom | Cause and fix | | --- | --- | | token rejected (401/403 on the directory probe) | The key was revoked or mistyped. Mint a fresh key in Settings → API keys and update config/org.yaml (or the COHORT_API_TOKEN env) | | key not paired to a member | The key exists but is not paired to this agent's workforce member. Pair it in Settings → API keys | | protocol drift warning | The server speaks a newer org protocol than this SDK vendors. Upgrade: npm i -g @cohortapp/agent-sdk@latest && npm run upgrade | | Pairing requested but the agent still cannot act | maestro pair <code> only opens the handshake — config/org.yaml stays disabled until an org admin approves it and you set the issued key (maestro setup --only org) | | No cached org context from maestro who-owns | The agent is enrolled but has never pulled context. Run maestro setup --only org, then maestro sync | | Workspace mailbox warning | An admin has not verified a domain and assigned this agent a mailbox in Settings → Email — the wizard warns but never blocks. A "SDK predates workspace email" warning means upgrade the SDK |

Runtime

| Symptom | Cause and fix | | --- | --- | | Daemon heartbeat stale (health.json older than 60 s) | The daemon is not running. launchctl list \| grep ai.maestro, then reload the daemon plist; check logs/ for the crash narrative | | Agent does nothing at all | Check for .emergency-stop in the repo root — it halts producer and consumer by design. Resume with scripts/resume-operations.sh | | Cadence work piling up in state/cadence-bus/inbox/ | Normal while the daemon is down — events drain on next start. If the daemon is up, read logs/cadence-bus/<date>.jsonl for failed/dlq rows | | Legacy plists flagged by doctor | The repo predates the cadence bus. npm run upgrade regenerates them and doctor prints the launchctl commands to roll the live jobs | | 401s in every sub-session | An invalid ANTHROPIC_API_KEY in .env. Fix the key — or, on a subscription machine, clear it and set MAESTRO_PREFER_SUBSCRIPTION_AUTH=1 so spawns ride keychain OAuth |

Model router

| Symptom | Cause and fix | | --- | --- | | config present but lib/model-router.mjs not reachable | Framework files are older than the config. npm run upgrade in the agent repo | | A backend never gets chosen — missing_credential in tried[] | The provider's key is neither in local env nor leasable from the org. Set it once at the workspace (Settings → Secrets) or locally in .env; a local key always wins | | Routing looks wrong | maestro router why <task_class> prints the decision, the chain, and every rejected candidate with the reason; maestro router validate strict-checks the config against the catalog | | Need Anthropic-only now | Export MAESTRO_ROUTER_FORCE_ANTHROPIC=1 (wins even over a corrupt config), or rename config/model-routing.yaml to .disabled |

Secrets

| Symptom | Cause and fix | | --- | --- | | No broker configured from maestro secrets sync | Local-only store, nothing to pull from. Set broker.base_url + token_env in config/secrets.yaml if your org runs a secret broker |

Fail-open is deliberate

Org connectivity, the credential broker, collective memory and the router all fail open: an outage degrades the agent to its local capabilities, it never halts it. The only hard stops are the human-written .emergency-stop file and the org-side kill switch.