Troubleshooting
Work from the sender toward the Mac: can the sender reach a hub, did the hub store the item, can the Mac see it, is the Mac showing the right context.
Reporting a problem: what to include, and how to save the app's log with tokens and invite codes masked, is in Testers → Report a problem.
Start here: needs-you doctor #
On the sender, run:
needs-you doctor # or ~/.local/bin/needs-you doctor if it isn't on PATH
needs-you doctor --json # the same, for agents: {ok, version, checks: [{check, status, detail, hint}]}It checks the env file (and that its mode is 600), whether needs-you and ~/.local/bin are on PATH, each hub URL (reachable, hub version, the token's name and role), the outbox (queued, failed, oldest), each agent's hooks or plugin (Claude Code, Codex, Gemini, opencode, Copilot, Kimi, Grok, Cursor, Cline, Aider), the Claude skill, the MCP server and agent instructions if you installed them, Orca settings, updates, and the 5-minute flush schedule. Each line is OK, WARN, FAIL or INFO. Every WARN and FAIL has one next step under it (after ->): a command to run as is, or exactly what to ask for, such as a new invite link (<invite link> is the only placeholder). It exits 1 if any check is FAIL. It's read-only: it never posts an item, never flushes the outbox and never prints the token (only "set" and its length).
Quick checks by hand #
# on the sender
. ~/.config/needs-you/env
for u in $(echo "$NEEDS_YOU_URLS" | tr ',' ' '); do
printf '%s ' "$u"; curl -sS -o /dev/null -w '%{http_code}\n' --max-time 5 "$u/v1/health"
done
ls ~/.local/state/needs-you/outbox/ 2>/dev/null # queued items that haven't reached a hub
tailscale status | head # is this machine on the tailnet?
# macOS with the Tailscale app: /Applications/Tailscale.app/Contents/MacOS/Tailscale statusA sender can't reach the hub #
Setting up Tailscale, or checking it step by step: Tailscale.
| Symptom | Likely cause | Fix |
|---|---|---|
curl: (6) Could not resolve host | MagicDNS off, or the machine isn't on the tailnet | tailscale up; enable MagicDNS in the admin console; tailscale status should list the hub |
curl: (7) Failed to connect / timeout | Hub down, or listening on a different address/port | On the hub: systemctl status needs-you-hub, journalctl -u needs-you-hub -n 50 (see Server hubs for the unit name) |
| Times out only from some machines | Tailscale ACL | Allow those machines (or their tag) to reach the hub's tag on tcp:8765 |
| Works by IP, not by name | DNS | Use the full MagicDNS name, <hub>.<tailnet>.ts.net |
HTTP 421 "doesn't answer to that host name" | The URL uses a name the hub doesn't know as its own (a custom DNS name or alias); this is its DNS-rebinding protection | Use the hub's public URL (MagicDNS name) or tailnet IP, or add the name to the hub's allowed_hosts (--allowed-host, or NEEDS_YOU_HUB_ALLOWED_HOSTS for the Mac app's hub, Server hubs) |
HTTP 401 / 403 | Wrong, revoked, or Mac-only token | Get a new invite link and re-run its installer with --force |
| Times out only while the Mac sleeps | The Mac's own hub is asleep | Expected: items queue and the 5-minute flush sends them after it wakes. Add a server hub to avoid the wait. |
| Times out from servers, works on the Mac | Tailscale is down on the Mac (the app's hub then listens only on 127.0.0.1), or the macOS firewall blocks it | Bring Tailscale up on the Mac; the hub picks up the tailnet address by itself (Settings… → Your inbox shows the URL). Allow python3 in System Settings → Network → Firewall |
HTTP 400 | Validation: title > 100 chars, body > 2,000, > 6 links, a link scheme not on the allow-list, a bad context/kind/priority | Fix the item; the response body says which field |
HTTP 429 or "too many open items" | The sender has 60 open items: something is looping | Stop the loop; resolve the stale keys |
The CLI never fails your job because of the hub. It queues to ~/.local/state/needs-you/outbox/ and sends on the next call or needs-you flush (the invite installer schedules one every 5 minutes). Items sitting in the outbox mean no hub has accepted them yet. The outbox keeps at most 500 requests and 7 days; older ones are dropped with a warning.
Invite links #
| Symptom | Cause | Fix |
|---|---|---|
This invite link is unknown, expired or revoked (exit 1) | What it says | Make a new link |
invite ... has no uses left (exit 1) | A new machine, or --force, on a used-up link | Make a new link, or one with more uses |
HTTP 429 / "too many failed invite attempts" | 10 failed tries from this IP in 10 minutes | Wait 10 minutes; check the link was pasted whole |
| "This invite (..., role owner) is for the Mac app" | An owner/reader link was used on a server | Open the needsyou:// link on the Mac; make a sender link for servers |
Re-running the one-liner (or --uninstall) fails after the link expired | Re-runs work until expiry, not after | Update with needs-you update; remove by hand (Add a sender); or make a new link |
curl: (22) ... 404 and no other output | A hub older than this release (it 404s dead links, and bash runs the empty script) | Make a new link; update the hub |
| "kept the existing token" | The machine was already set up | Expected. --force redeems again and replaces the token |
| Installer says no hub answered | The hub is asleep or unreachable right now | The setup still completed; the test item is queued |
Not set up: Gemini CLI hooks ... (or Codex, Claude Code, opencode, the skill) at the end; exit 3 if nothing you asked for was set up | That agent's config couldn't be changed (unreadable JSON such as comments in settings.json, a symlink, a failed download); the message above it says which | Fix the file it names, then re-run the one-liner with the flag the line gives. The CLI and token are already set up |
setup-sender.sh (manual setup) #
needs-you: command not foundafter setup:~/.local/binisn't on yourPATH. Addexport PATH="$HOME/.local/bin:$PATH"to your shell profile. Hooks and cron don't read your profile; they find the CLI at~/.local/bin/needs-youdirectly (or setNEEDS_YOU_BIN).- "no terminal available; continuing as --non-interactive": you ran it without a TTY (e.g. over
ssh host cmd). Usessh -t, or pass--urland--token-stdin. - "does not look like the needs-you CLI": the
--install-cliURL returned an HTML page (login wall, 404). Use the raw file URL. - Health ok, but the test item is refused (401): the token isn't valid on that hub. Tokens replicate between peered hubs; check the hubs are peered and the token exists on each (
needs-you-admin token list).
Items post, but nothing shows on the Mac #
- Context and hours: a
workitem at 21:00 shows only as the faint second number (0 · 1). Use the toggle in the expanded header, or check the work-hours setting. - Kind:
doneandinfoitems never raise the count. They're in the collapsed Recent section. - Snoozed: press Control-Option-Space (⌃⌥Space, or your configured shortcut) to bring the panel back.
- Mac can't reach a server hub: the Mac needs Tailscale up. Hover the idle pill: the "last check" time should be recent.
- Wrong hub: the sender's
NEEDS_YOU_URLSmust include the Mac's hub or a server hub peered with it. - Hubs out of sync: if the Mac polls
hub-aand the sender wrote tohub-b, replication should copy it within seconds. If it doesn't, check the peer list and the replication outbox onhub-b(Server hubs).
Duplicate or stale cards #
- Duplicates: the sender changes its key between runs (a timestamp, a run id). Keys must be stable.
- Cards that never go away: the sender never calls
resolve. Fix the sender, then clear the card with Done on the Mac. - A sender that went away (a killed agent session, a machine that's gone, an automation that crashed) can't resolve its cards. Safety nets, in the order they kick in:
- Agent hooks record the agent's process. The 5-minute
needs-you flushon that machine resolves the card once the process is gone (Claude Code). - Agent cards expire 48 hours after their last post (
NEEDS_YOU_AGENT_EXPIRY_HOURS; Aider's after an hour). Each new post pushes it out. - Scheduled automations post with
--expires-inof about twice their interval, so a blocker they stop reporting drops off by itself (Orca). - On the Mac, a card waiting 4 hours or more shows its age, and its … menu has Dismiss All from <host> for a machine that went away (Mac app).
- Agent hooks record the agent's process. The 5-minute
- A card keeps re-animating: the title, body or priority changes on every post (e.g. a counter or time in the title). Put changing details in the body sparingly, or keep them out.
Claude Code hooks #
Setup per environment (SSH, tmux, VS Code Remote-SSH, Orca): Claude Code everywhere. Turn on the debug log and simulate an event:
echo '{"session_id":"t1","cwd":"'"$PWD"'","notification_type":"idle_prompt","message":"test"}' |
NEEDS_YOU_AGENT_ALERTS=1 NEEDS_YOU_HOOK_LOG=/dev/stderr ~/.claude/hooks/needs-you-hook.sh notify| Log says | Meaning |
|---|---|
| nothing at all | Not opted in. Set NEEDS_YOU_AGENT_ALERTS=1, or run inside Orca. Check it isn't 0 in ~/.config/needs-you/env. |
needs-you CLI not found | Install it (an invite link, or setup-sender.sh --install-cli) or set NEEDS_YOU_BIN. |
notify agent:... -> 0 | Posted (or queued). Check the hub and the Mac as above. |
notify agent:... -> 1 | The CLI failed. Run the same needs-you add by hand to see the error. |
Other checks:
/hooksinside Claude Code lists the active hooks. If ours are missing, re-runinstall-hooks.shand restart the session.- Project-level hooks use
$CLAUDE_PROJECT_DIR/.claude/hooks/needs-you-hook.sh. If the repo was cloned without.claude/hooks/, re-runinstall-hooks.sh --project. - A card that doesn't clear: the resolve runs only if this session posted (marker files in
~/.local/state/needs-you/claude-hooks/). Deleting that directory is safe. A session that was killed is cleared by the nextneeds-you flushonce its Claude process is gone (needs-you doctorshows whether the flush is scheduled), or 48 hours after its last post. - Too noisy?
idle_promptfires after about a minute of waiting. Opt in only on machines where agents run unattended.
Codex hooks #
needs-you doctor has a codex hooks line. Simulate an approval prompt and its resolve with a log to stderr:
echo '{"hook_event_name":"PermissionRequest","session_id":"t1","cwd":"'"$PWD"'","tool_name":"Bash","tool_input":{"command":"make test"}}' |
NEEDS_YOU_AGENT_ALERTS=1 NEEDS_YOU_HOOK_LOG=/dev/stderr ~/.codex/hooks/needs-you-hook.sh notify codex
echo '{"session_id":"t1"}' | NEEDS_YOU_AGENT_ALERTS=1 NEEDS_YOU_HOOK_LOG=/dev/stderr ~/.codex/hooks/needs-you-hook.sh resolve codexThe log lines mean the same as for the Claude Code hooks above. Codex-specific causes:
- Not trusted. Codex skips hooks nobody has reviewed and says so at startup. Open
/hooksin Codex and trust the needs-you entries. Editing those entries by hand makes them untrusted again. - Hooks turned off.
hooks = falseunder[features]in~/.codex/config.toml(or a managedrequirements.toml) disables every hook; doctor reports the first. - Another
CODEX_HOME. The installer and doctor follow$CODEX_HOME; run them with the same value Codex uses. - Every turn makes a card. That's the
Stophook: Codex finished and is waiting for you. Opt in only where Codex runs unattended, setNEEDS_YOU_AGENT_TURN_CARDS=0to keep only approval cards, or turn it off for a session withNEEDS_YOU_AGENT_ALERTS=0.
Copilot CLI hooks #
needs-you doctor has a copilot hooks line. In copilot mode the hook finishes in the background, so log to a file:
echo '{"sessionId":"t1","cwd":"'"$PWD"'","notification_type":"permission_prompt","message":"Run command: make test"}' |
NEEDS_YOU_AGENT_ALERTS=1 NEEDS_YOU_HOOK_LOG=/tmp/ny-hook.log ~/.copilot/hooks/needs-you-hook.sh notify copilot
sleep 2; cat /tmp/ny-hook.logCopilot-specific causes:
- Not restarted. Copilot CLI reads
~/.copilot/hooks/*.jsonwhen it starts. - Hooks turned off.
"disableAllHooks": truein~/.copilot/settings.jsonorconfig.json; doctor reports it. - Another
COPILOT_HOME. Copilot then reads$COPILOT_HOME/hooks/instead; run the installer and doctor with the same value. - A card stays after Esc. Cancelling a permission prompt runs no hook in Copilot; the card goes with your next prompt or the end of the session.
Kimi Code hooks #
needs-you doctor has a kimi hooks line, and kimi doctor checks that Kimi accepts config.toml. In kimi mode the hook finishes in the background, so log to a file:
echo '{"hook_event_name":"PermissionRequest","session_id":"session_t1","cwd":"'"$PWD"'","tool_name":"Bash","display":{"command":"make test"}}' |
NEEDS_YOU_AGENT_ALERTS=1 NEEDS_YOU_HOOK_LOG=/tmp/ny-hook.log ~/.kimi-code/hooks/needs-you-hook.sh notify kimi
sleep 2; cat /tmp/ny-hook.logKimi-specific causes:
- Not restarted. Kimi reads
config.tomlwhen it starts. - Kimi refuses the config. Any key Kimi doesn't know, in any
[[hooks]]entry, makes it refuse the whole file;kimi doctornames it. The needs-you block uses onlyevent,matcher,commandandtimeout. - The installer stopped. Your
config.tomldefineshooksas a table or an inline list: copy the entries fromintegrations/kimi/kimi-hooks.tomlinto it by hand. - Another
KIMI_CODE_HOME. Kimi then reads$KIMI_CODE_HOME/config.toml; run the installer and doctor with the same value. - No "waiting" card from
kimi -p. By design: the run has exited, so nobody is waiting (the log saysskipped: kimi has exited). - A card stays after Ctrl-C. Kimi runs no
SessionEndhook then; the 5-minuteneeds-you flushclears the card once Kimi has exited. Leave with/exitto clear it at once.
Grok Build hooks #
needs-you doctor has a grok hooks line, and grok inspect lists the hooks Grok loaded. In grok mode the hook finishes in the background, so log to a file:
echo '{"hook_event_name":"Notification","notificationType":"permission_prompt","session_id":"t1","cwd":"'"$PWD"'"}' |
NEEDS_YOU_AGENT_ALERTS=1 NEEDS_YOU_HOOK_LOG=/tmp/ny-hook.log ~/.grok/hooks/needs-you-hook.sh notify grok
sleep 2; cat /tmp/ny-hook.logGrok-specific causes:
- The "waiting" card comes a minute late. By design: it comes from Grok's
idle_promptnotification, about 60 seconds after the turn, and not at all if you type first. - Not restarted, or switched off. Grok reads
~/.grok/hooks/*.jsonwhen it starts; an entry switched off in/hooksstays off (~/.grok/disabled-hooks), and an organization policy withallow_managed_hooks_onlyturns off every user hook. Doctor reports both. - Cards say Grok but come from the Claude Code hooks. Without needs-you's own Grok hooks, Grok runs the Claude Code ones, which post for it. With both installed, only the Grok ones post.
- Another
GROK_HOME. Grok then reads$GROK_HOME/hooks/; run the installer and doctor with the same value.
Cursor, Cline and Aider #
needs-you doctor has cursor hooks, cline hooks and aider notifications lines. None of the three has an approval hook, so no card when the agent asks to run something: that's expected, not a fault. The hook finishes in the background in all three, so log to a file (NEEDS_YOU_HOOK_LOG=/tmp/ny-hook.log on the test commands in Cursor, Cline or Aider).
- Opted in only in your shell. Cursor and Cline in VS Code start hooks from the app, which doesn't read your shell profile. Put
NEEDS_YOU_AGENT_ALERTS=1in~/.config/needs-you/env. - Cursor: a prompt seems held up. The hook answers
beforeSubmitPromptwith{"continue":true}before anything else. If you see it held anyway, check~/.cursor/hooks.jsonfor a needs-you entry under a permission hook (preToolUse,beforeShellExecution, …): doctor warns about it; re-run the installer. - Cline: no card in VS Code. Cline's "Enable Hooks" setting is off, or one of your own hook files took the
TaskCompletename (doctor says which). - Cline: a card stays after you quit the CLI. Interactive
clinesessions run in a background hub process that outlives the terminal; the card goes with your next task, when that process stops, or after 48 hours. - Aider: no card. A
.aider.conf.ymlin the repo, orAIDER_NOTIFICATIONS_COMMAND, overrides the one in your home directory; ornotifications: trueis missing (Aider needs both keys). If the installer printed two lines instead of writing them, add them yourself. - Aider: the card stays while you type. Aider sends nothing when you answer. It goes when Aider exits (within 5 minutes, by
needs-you flush) or afterNEEDS_YOU_AIDER_EXPIRY_HOURS.
GitHub poller #
needs-you-github -vprints what it saw (notifications, PRs) and anygherror. It always exits 0, so a broken cron job is silent; after 3 failed runs in a row it posts one low card, "GitHub alerts stopped on <host>".gh: Bad credentialsorgh auth loginerrors: log in again as the user cron runs as. cron and systemd have a shortPATH; setNEEDS_YOU_GITHUB_GHto the full path ofghif it isn't in/usr/bin.- Too many cards: narrow it with
NEEDS_YOU_GITHUB_EXCLUDE,NEEDS_YOU_GITHUB_REASONSorNEEDS_YOU_GITHUB_PR_DAYS(integrations/github). - A card that doesn't clear: it clears on the run after the condition goes away, or 15 minutes after the poller stops. Notification cards clear when you read the thread on GitHub.
Orca automations #
needs-youmust be on thePATHthat Orca's agent terminals get. From an Orca terminal:command -v needs-you.- An automation that stopped posting after a template re-render: the block was added to the live prompt, not the template.
- An
orca://link refused with 400: the hub no longer acceptsorca://at all (Orca has no terminal or worktree links; its only link,orca://skills/share/<id>, imports a skill). Remove it from the automation prompt orNEEDS_YOU_AGENT_LINK; the card's Terminal button (needsyou://orca/terminal) and the body'sorca terminal switchcommand are the way back to the terminal. - A
vscode://orcursor://link refused with 400, or shown as plain text on an older card: onlyvscode://file/<abs path>[:line[:col]],vscode://vscode-remote/ssh-remote+<host>[/<path>](ortunnel+<name>) andvscode://anthropic.claude-code/open?session=<id>are allowed, without a query string (API). The Claude Code hook posts its card again without links when the hub refuses a customNEEDS_YOU_AGENT_LINK. orca terminal switchsays the terminal isn't found: the agent runs on a paired Orca server. SetNEEDS_YOU_ORCA_ENVIRONMENTin~/.config/needs-you/envon that server to the nameorca environment listshows on the Mac.
This page is docs/guides/troubleshooting.md in the repo. View or edit it on GitHub.