Symptom: Stop hooks (Telegram notifications, KB sync) silently fail. Check /tmp/ or hook logs for node: command not found.
Cause: Headless sessions (claude -p) and hooks run in a minimal shell that doesn't load your profile. node isn't in the default PATH.
Fix: Use the full path to node in all hook commands in ~/.claude/settings.json:
// Bad: "command": "node $HOME/claude-fleet/notify-human.js"
// Good: "command": "/opt/homebrew/bin/node $HOME/claude-fleet/notify-human.js"Find your node path with which node and update all hook entries.
Symptom: claude: command not found when running via SSH.
Cause: SSH non-interactive shells don't load your full shell profile, so PATH may not include the Claude binary.
Fix: Use the full path in get_claude_cmd():
# macOS (Homebrew)
/opt/homebrew/bin/claude
# macOS (App bundle)
~/Library/Application\ Support/Claude/claude-code/<version>/claude.app/Contents/MacOS/claude
# Find it on your system
which claudeSymptom: Hook scripts can't find files at expected paths. Errors like /root/claude-fleet/...: No such file or directory.
Cause: bash on Windows may point to C:\Windows\System32\bash.exe (WSL), which has a completely different filesystem.
Fix: Use node for hooks on Windows instead of bash. The notify-human.js script works natively on Windows.
Symptom: git pull fails with permission denied, even though the key is on GitHub.
Possible causes:
-
Key has a passphrase. SSH can't prompt for it in non-interactive mode. Generate a new key without a passphrase:
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
-
Key file permissions too open. Windows OpenSSH rejects keys that Administrators/SYSTEM can read:
icacls %USERPROFILE%\.ssh\id_ed25519 /inheritance:r /remove "BUILTIN\Administrators" /remove "NT AUTHORITY\SYSTEM" /grant:r "%USERNAME%:(R)"
-
Git using HTTPS instead of SSH. The Windows credential manager (
wincredman) needs a TTY:git remote set-url origin git@github.com:you/fleet-kb.git
Symptom: CONFLICT (content): Merge conflict in daily/... or inbox files.
Fix: For most fleet files, either side's version is fine:
git rebase --skip # skip the conflicting commit
# or
git checkout --theirs . && git add -A && git rebase --continueChecklist:
- Verify token/chat ID:
curl -s "https://api.telegram.org/bot<TOKEN>/getMe"— should return bot info - Test send:
curl -s -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" -d chat_id="<ID>" -d text="test" - Check the
.envfile path matches what the script expects (~/claude-fleet/fleet.envor~/.ccgram/.env) - On Windows, verify
nodeis in PATH:where node
Symptom: Telegram shows
Causes:
- The SessionStart hook consumed turns (git pull, inbox processing)
- Complex tasks need more turns
Fix: Increase --max-turns in fleet-inbox-check.sh, or re-trigger the specific machine:
./fleet-inbox-check.sh betaChecklist:
- Is Tailscale running on both machines?
tailscale status - Is the machine awake/powered on?
- Is SSH enabled?
tailscale up --ssh - Test basic connectivity:
ping <machine-name>
Symptom: Claude Desktop shows permission prompts (tool approval dialogs) during headless or automated sessions, causing them to hang indefinitely.
Cause: Claude Code requires explicit approval for certain tools by default.
Fix: Enable bypassPermissions mode in ~/.claude/settings.json:
{
"permissions": {
"defaultMode": "bypassPermissions",
"deny": [
"Bash(rm -rf /)",
"Bash(sudo rm -rf *)"
]
},
"skipDangerousModePermissionPrompt": true
}Both fields are required:
defaultMode: "bypassPermissions"— skips interactive permission promptsskipDangerousModePermissionPrompt: true— skips the one-time "are you sure?" confirmation
Only enable this on machines you trust — it allows Claude to run any tool without asking.
Known behaviors in bypass mode:
- Editing
~/.claude/CLAUDE.mdalways prompts (built-in safeguard — prevents agents from silently rewriting their own instructions) - If you deny any single permission prompt during a session, Claude Code switches to prompting for ALL subsequent tool calls. The session cannot recover — start a new one.
Symptom: You click an inline button (approve/reject) in Telegram but get "Session not found" or "Expired."
Cause: The approval session timed out. By default, callback sessions expire after a few minutes. If the bot restarts or enough time passes, the session context is lost.
Fix: Use bypassPermissions mode (above) to avoid needing Telegram approval in the first place. If you need approval workflows, ensure the bot stays running persistently and process approvals quickly.
Symptom: You pushed a task to inbox/alpha.md and triggered the machine, but nothing happened. The inbox item is still pending.
Causes:
- Machine name mismatch. The script looks for
inbox/<machine-name>.md. If the hostname doesn't match the inbox filename, it finds nothing.- Fix: Set
FLEET_MACHINE_NAMEexplicitly. See Machine Name Detection.
- Fix: Set
- KB not pulled. The machine's local copy of
~/knowledgeis stale.- Fix: Verify with
cd ~/knowledge && git log --oneline -1— does it show the commit with your inbox item?
- Fix: Verify with
- Hook not installed. The SessionStart hook isn't configured.
- Fix: Check
~/.claude/settings.jsonfor the SessionStart hook entry.
- Fix: Check
Symptom: The session-end hook (kb-session-end.sh) fails to push changes. Work may be committed locally but not shared.
Causes and fixes:
-
Network unavailable. The machine is offline or Tailscale is down.
- Fix: Check
tailscale status. Changes are committed locally and will push on the next successful sync.
- Fix: Check
-
Credential issues. SSH key not loaded, expired token, etc.
- Fix: Test manually:
cd ~/knowledge && git push. Fix any auth errors.
- Fix: Test manually:
-
Merge conflicts. Another machine pushed first and the rebase failed.
- Fix: Pull and resolve manually:
cd ~/knowledge git pull --rebase # Resolve any conflicts, then: git push
-
Remote rejected (branch protection, etc.).
- Fix: Ensure the git user has push access to the KB repo's default branch.