Troubleshooting
Common Watchfire failure modes and copy-paste fixes — daemon won't start, GUI can't reach watchfired, stuck tasks, dirty merges, sandbox denials, and where to ask for help.
A scannable answer to "something broke — what now?" Each section names a symptom, the most likely cause, and a fix you can paste into a terminal. For the bigger-picture model behind any of these, follow the cross-links into Architecture, Sandboxing, Security, and the matching command reference.
If your problem isn't here, jump to Where to ask for help.
Daemon won't start
The CLI says daemon failed to start within timeout, or watchfired
exits the moment you launch it.
Cause
The daemon is started silently in the background by the CLI and the GUI, so a startup failure (binary missing, port collision on the inbound HTTP server, stale state file with a live PID) is invisible until you run it yourself in the foreground.
Fix
Run the daemon directly so its stderr lands in your terminal:
watchfired --foreground
If startup output complains that the inbound HTTP server
can't bind, another process is already on 127.0.0.1:8765 — set a
different ListenAddr in InboundConfig, or stop the conflicting
process.
If startup is wedged on a stale ~/.watchfire/daemon.yaml (the file
that records the running daemon's host, port, and PID — see
Architecture → Network),
remove it and start clean:
rm ~/.watchfire/daemon.yaml
watchfire daemon start
Watchfire normally clears this file itself on launch when the recorded PID is gone, so only delete it manually if the daemon refuses to come back up.
CLI/TUI can't connect to daemon
watchfire status (or any project-scoped command) fails with
connection refused or hangs without producing output.
Cause
The daemon process exited but the connection-info file is still around,
or the PID in ~/.watchfire/daemon.yaml points at a different process
on a recycled PID.
Fix
Confirm the daemon's view of itself first:
watchfire daemon status
If it reports the daemon is not running, start it explicitly:
watchfire daemon start
If it reports a port and PID but the CLI still can't connect, the recorded PID is stale. Stop and restart cleanly:
watchfire daemon stop
watchfire daemon start
See watchfire daemon for what each subcommand
does, and the daemon command pitfalls
for related quick fixes.
GUI shows "no daemon"
Watchfire.app opens, but the project list is empty and the header
shows the daemon as unreachable.
Cause
The GUI launches watchfired itself, walking PATH first and then
falling back to bundled and common install paths. If none resolve to a
working binary, the GUI surfaces the connection error without launching
anything.
Fix
Confirm the binary is actually on PATH from a normal terminal:
which watchfired
watchfired --version
If which finds nothing, install or reinstall Watchfire — see
Installation. The GUI also checks
/opt/homebrew/bin/watchfired and /usr/local/bin/watchfired as
last-resort fallbacks, so a Homebrew install is enough on macOS.
If the binary exists but the GUI still won't start it, launch the daemon yourself before opening the app:
watchfire daemon start
The GUI will pick up the running daemon on its next reconnect attempt. The Daemon component page covers how clients discover the running daemon.
Tasks stuck after a crash
A task's agent_sessions counter is non-zero and started_at is set,
but no agent is running and status is still ready (or a stuck
draft you didn't write).
Cause
If the daemon crashes mid-task, the daemon process terminates without
marking the task done. Watchfire's task statuses
are only draft, ready, or done — there is no running status to
clean up, but a task that started can sit half-finished if no one
restarts it.
Fix
Open the task YAML at
<project>/.watchfire/tasks/<task_number>.yaml (the path is detailed
in Projects and Tasks → Directory Structure)
and verify status: ready. Then re-run it:
watchfire run <task_number>
If the task should be skipped instead, set status: done and
success: false with a failure_reason. If you want the agent to
review and re-shape it first, set status: draft and let the
Refine phase pick it up on the next
wildfire cycle.
Worktree won't merge
A task completes with success: true but the changes never land on
your default branch. The task YAML has a populated
merge_failure_reason, and a watchfire/<task_number> branch is still
hanging around with the work on it.
Cause
Auto-merge runs after the agent marks the task done. It bails when the default branch has uncommitted changes, when the merge would conflict, or when the worktree was deleted out from under the daemon. The work is fine — the merge step is what failed.
Fix
First, take stock from the project root:
git status
git branch --list 'watchfire/*'
If the default branch is dirty, commit or stash, then merge the task branch by hand:
git merge watchfire/<task_number>
If the merge conflicts, resolve them in the default branch checkout
— not inside .watchfire/worktrees/<task_number>/. Touching files
inside the worktree directly fights the daemon's bookkeeping (it owns
that directory and prunes it on cleanup — see
Git Worktree Isolation). Once merged, the
daemon's next cleanup pass will remove the orphaned worktree.
If you'd rather review every merge by hand instead of fighting the
auto-merge path, set auto_merge: false in .watchfire/project.yaml
(reference). The
agent still finishes its work in watchfire/<task_number>; you merge
when you're ready.
Agent backend not authenticated
The agent terminal shows Please run /login, OAuth token has expired, or a 401 authentication_error. Watchfire flags the session
as needing attention.
Cause
Watchfire reuses each backend's own login. It does not store API keys of its own and does not paper over expired sessions — when the backend's auth fails, the session fails. Watchfire detects this from the agent's terminal output and surfaces it to the TUI and GUI.
Fix
Re-authenticate the affected backend in its own CLI, outside Watchfire:
| Backend | Re-auth flow |
|---|---|
| Claude Code | Run claude and use the in-app /login command, or follow the Claude Code login flow |
| OpenAI Codex | Run codex and complete the Codex login prompt |
| opencode | Sign in via opencode's normal flow so ~/.config/opencode/ is current |
| Gemini CLI | Re-auth Gemini CLI directly so ~/.gemini/ reflects a valid session |
| GitHub Copilot CLI | Sign in with gh or Copilot CLI so ~/.copilot/config.json is valid |
Once the backend works in its own terminal, restart the failed Watchfire session — the new auth flows in automatically because Watchfire references your real config. The full per-backend setup matrix is on Supported Agents.
Sandbox denial — "Operation not permitted"
The agent prints Operation not permitted (macOS) or generic
permission errors when it tries to write outside the worktree, write
to .env, or touch .git/hooks.
Cause
The agent process runs inside a platform sandbox. macOS Seatbelt blocks
.env, .git/hooks, credential dirs (~/.ssh, ~/.aws, ~/.gnupg,
~/.netrc, ~/.npmrc), and personal dirs (~/Desktop, ~/Documents,
~/Downloads, ~/Music, ~/Movies, ~/Pictures). Linux Landlock
and Bubblewrap block the same credential dirs. The full matrix is on
the Sandboxing page.
Fix
First, decide whether the denial is correct. The sandbox catches
real exfiltration attempts and AI hallucinations equally well —
denying a write to ~/.ssh is a feature, not a bug. The
Security threat model lays out
what the sandbox is supposed to block.
If the denial is wrong (the agent legitimately needs to touch a path outside the worktree), the fastest unblock is to run the session without the sandbox:
watchfire run <task_number> --no-sandbox
--no-sandbox is also wired into watchfire wildfire,
watchfire generate, and
watchfire chat. The
Sandboxing → Configuration section
lists every flag and project-level setting.
The cleaner long-term fix is usually to rewrite the task prompt so the agent does its work inside the worktree — see Tips & Best Practices for prompt patterns that keep agents in scope.
Wildfire mode runs forever
You start watchfire wildfire and it never stops, even though the
project feels done.
Cause
Wildfire only exits when all three phases
yield nothing: no ready tasks to execute, no draft tasks to
refine, and the generate phase produced no new tasks. Anything that
keeps drafting (an over-eager generate phase, a refine pass that
churns the same task) keeps the loop alive. Wildfire and start-all
also have restart protection:
3 consecutive failures on the same task drop the loop into chat mode
instead of looping forever on a broken task.
Fix
Stop wildfire with Ctrl+C in the session that launched it. The current agent terminates gracefully and the loop exits.
If wildfire keeps regenerating the same kind of work, tighten the project definition before re-running:
watchfire define
watchfire define opens the definition
in $EDITOR. A specific definition produces a specific task list —
see Tips & Best Practices for what works, and the
wildfire command pitfalls
for related quick fixes.
Generate phase produces empty or low-quality tasks
watchfire generate finishes but no useful
tasks land in .watchfire/tasks/, or the generated tasks are vague.
Cause
Generate uses the project definition as its primary input. An empty or generic definition produces empty or generic tasks.
Fix
Run watchfire generate first to bootstrap a definition from the
codebase, then refine it before generating tasks:
watchfire generate # populate project.yaml definition from the codebase
watchfire define # tighten it in $EDITOR
watchfire plan # generate a fresh task list
watchfire generate and watchfire plan are both documented on the
generate command page (see also
generate pitfalls). The
definition command page covers what
belongs in a good definition.
Beacon notifications not firing
You enabled OS toasts in settings, but TASK_FAILED and
RUN_COMPLETE events never reach you.
Cause
Every notification path funnels through models.ShouldNotify, which
combines the master toggle, per-event toggle, quiet-hours window, and
the per-project mute list before any toast, sound, or relay fires.
Any one of those gates being off silences the event. The
GUI Notifications panel and the
TUI globalsettings tab read and write the same fields in
~/.watchfire/settings.yaml.
Fix
Open global settings (the GUI Settings panel, or Ctrl+g in the TUI) and confirm:
- Master notifications is on
- The specific event kind (
TASK_FAILED,RUN_COMPLETE,WEEKLY_DIGEST) is on - Quiet hours doesn't currently overlap the time you're testing
- The project isn't on the per-project mute list
For the deeper Beacon model — bus, JSONL fallback log, sound gating — see the daemon notifications section.
Webhook signature verification fails
An inbound integration (Slack,
Discord, GitHub) returns 401. The Slack or Discord upstream reports
the request as rejected; your slash command never runs.
Cause
Slack and Discord both enforce a 5-minute timestamp drift window on the signed payload, and every verifier uses constant-time comparison against the configured secret. Three things break this in practice: the wrong secret was pasted into the GUI, the host clock has drifted more than 5 minutes from the upstream's clock, or the upstream's signing secret was rotated and Watchfire still has the old one.
Fix
Re-paste the signing secret from the upstream's app dashboard into the GUI Integrations panel — secrets are write-only on the wire, so you can replace them but not read the current value back. Then re-send the test event from the upstream.
Confirm the host clock is current — Slack and Discord both reject requests outside the 5-minute drift window:
date -u
If the clock is off, fix it through your OS time settings; the sandbox doesn't gate clock APIs.
watchfire update failure
watchfire update fails to download, fails
to swap binaries, or leaves you on the old version.
Cause
watchfire update queries the GitHub Releases API and atomically
replaces both the CLI and the daemon. It fails on no network, on a
GitHub rate limit, or on a binary path the current process can't
overwrite (uncommon — typically a permission issue on
/usr/local/bin/).
Fix
Confirm GitHub Releases is reachable:
curl -fsSL https://api.github.com/repos/watchfire-io/watchfire/releases/latest > /dev/null
If that works but the update still fails, fall back to a source install:
git clone https://github.com/watchfire-io/watchfire.git
cd watchfire
make install
Installation → Build from Source
covers prerequisites. The GUI updates separately via its built-in
electron-updater and is unaffected. See also the
update command pitfalls.
Where to ask for help
Once you've ruled out the cases above:
- Reproducible bug —
open an issue
using the bug report template. Include the output of
watchfire daemon statusandwatchfire status, your OS and kernel version, the relevant log file from~/.watchfire/logs/<project_id>/(the JSONL transcript is the readable one), and the exact steps to reproduce. - Question or open-ended discussion — start a thread in GitHub Discussions.
- Security vulnerability — do not file a public issue. Follow the disclosure flow on Security → Reporting a vulnerability.
Security
Watchfire's threat model — sandbox guarantees, signature verification, secret storage, network exposure, and how to report a vulnerability.
Tips & Best Practices
Hard-won advice for writing tasks agents finish, sizing work, picking modes, keeping merges clean, and getting the most out of Beacon — distilled from running Watchfire across real projects.