Troubleshooting¶
This page lists what can go wrong, as symptom, check and fix. The messages are quoted from the scripts, so you can
search this page for the line you see. Most scripts also write what they did to Anna's stream: anna-stream recent is
often the fastest first look.
Least-tested parts
The Telegram bot, the relay and headless login with a token haven't yet been walked on a clean machine. If you hit something there that isn't on this page, it's probably a gap in the scaffold, not something you did.
First: run the tests¶
The test suites check each piece in throwaway folders (their own config, state and record); they don't touch your install. From the clone:
cd ~/projects/anna
tests/run-all
You should see one line per suite and a summary:
ok test_body_checks 17 ok
ok test_clock 20 ok
...
ok test_relay 11 ok
all suites passed
A failing suite prints FAIL <suite> with its failed checks indented under it, and tests/run-all exits 1. For the
full output of every suite, tests/run-all -v. Without bun, the relay suite is skipped (skip test_relay … (no bun: …)).
If the tests pass but your install misbehaves, the difference is in your machine or config, which narrows it down.
Where to look¶
| What | Command |
|---|---|
| Anna's stream (every part logs here) | anna-stream recent (--hours 48, --from anna-day) |
| A timer's last run | systemctl --user status anna-morning (or anna-evening, anna-day-close, anna-day-open, anna-clock) |
| A timer's output | journalctl --user -u anna-morning -n 50 |
| Headless run reports | ls -t ~/anna-record/journal/handoffs/ |
Clock --run tasks' output |
<paths.state>/anna-run.log |
| The brain's screen | screen -r anna (detach: Ctrl-A, then D) |
| The bot's screen | screen -r anna-tg-<instance> |
| Which config is in use | anna-config --path, and anna-config --dump for every value |
Install¶
install.sh: 'screen' is required (the brain lives in a screen session)¶
Or the same for python3, jq, git, claude, or bun (for the relay and the Telegram channel). Install the
named program (Requirements) and run ./install.sh again.
For bun: it installs into ~/.bun/bin; open a new terminal so that's on your PATH, or turn off bot.enabled and
relay.enabled if you don't want them yet.
install.sh: Python 3.11+ is required¶
python3 --version is older than 3.11 (Anna's scripts read TOML with tomllib, new in 3.11). Use a newer distribution
release or install a newer Python.
Wrote … anna.toml from the example: edit it …¶
Not an error: there was no config, so the installer copied the example and stopped without installing anything.
Edit ~/.config/anna/anna.toml and run ./install.sh again.
The record was created but not committed¶
install.sh: created /home/you/anna-record but couldn't commit it (git user.name/email set?)
Git doesn't know who to author the commit as. The record folder now exists, so the installer won't create it again: commit it yourself, setting the name in the record only:
git -C ~/anna-record config user.name "Your Name"
git -C ~/anna-record config user.email "you@example.com"
git -C ~/anna-record add -A && git -C ~/anna-record commit -m "record: start from the Anna template"
cd ~/projects/anna && ./install.sh
install.sh: couldn't merge the hooks into ~/.claude/settings.json (left unchanged)¶
Your ~/.claude/settings.json isn't valid JSON. Check with jq . ~/.claude/settings.json, which names the line. Fix it
(a timestamped backup settings.json.bak.* sits beside it), then rerun.
~/CLAUDE.md exists: the template is at ~/CLAUDE.md.anna-new to merge by hand¶
You already had a ~/CLAUDE.md, and the installer doesn't overwrite it. The brain reads ~/CLAUDE.md at every start, so
copy the sections from ~/CLAUDE.md.anna-new into it. Same idea for ⚠ you edited: anna-goals.md (left as is; new
versions beside them as .anna-new): compare ~/.claude/agents/<name>.md with its .anna-new and merge.
⚠ … memory is a real directory: move its files into … memory and replace it with a symlink by hand¶
Claude Code already had a memory folder for your home directory. Move its files into <record>/memory/, remove the
empty folder, and rerun ./install.sh, which then creates the symlink.
⚠ user lingering is off: timers stop when you log out¶
Run sudo loginctl enable-linger $USER once. Without it, nothing runs at night unless you're logged in.
anna-day: command not found¶
~/bin isn't on your PATH yet. Open a new terminal or log in again (most distributions add ~/bin when it exists).
Check with echo $PATH.
Timers run but nothing happens, status=203/EXEC¶
The unit can't find the script it runs. systemctl --user cat anna-morning shows the path in ExecStart=; compare it
with paths.bin (anna-config paths.bin). If you changed paths.bin, run ./install.sh again: it writes the new path
into the units.
anna-config: no such key: …¶
A script asked for a key that isn't in the defaults, or you typed one wrong. Every key and its spelling: Configuration.
The brain¶
open: Claude Code asks you to accept Bypass Permissions mode …¶
Expected the first time on each machine. screen -dr anna, read the dialog, choose Yes if you agree, detach (Ctrl-A, D),
run anna-day open. Why: Security model § Allowlists.
open: Anna-… didn't reach the prompt in 80 s (screen -dr anna)¶
Claude Code started but didn't show its ready prompt. Attach with screen -dr anna and look: often a login prompt, an
update notice or a question. Answer it, then use the session, or exit and run anna-day open again.
open: trust prompt for an unexpected folder; left waiting¶
The trust prompt named a folder other than your home folder. anna-day only answers it for $HOME. Attach and read
it. The brain should start in ~.
open: couldn't select 'Yes' on the trust prompt; left waiting¶
The arrow key didn't register. Attach (screen -dr anna), choose "Yes, I trust this folder" yourself, detach.
open: yesterday's session still open; no new session¶
At open, yesterday's session was still busy (active in the last day.idle_min minutes), so anna-day left it alone
instead of starting a second one. Finish your work, then /anna-wrapup and /exit in it (press q at the resume
prompt), and anna-day open.
close: session … active N min ago; left open¶
Not an error: you were still working at day.close. The 04:15 open tries the close again.
close: wrap-up of … didn't finish in 40 min; left open¶
The overnight wrap-up turn didn't end. Attach and see what it's doing; often it's waiting on something. The relay stays
paused until the next anna-day open.
claude exited 3 times within 15 s (rc …). Shell here; resume by hand: claude --resume <id>¶
Claude Code crashes right at start. Run claude --version and claude in that shell to see the error (login expired,
an update, a broken setting in ~/.claude/settings.json). Once claude starts, type exit to close the window and run
anna-day open.
The brain blocks a command: anna-gate: this looks like a state change on …¶
Working as designed: the action gate's hook stopped a state change that had no recent go from the anna-action-gate
engine. The brain should call the gate and then retry, or ask you. If the hook blocks something it shouldn't (a read),
that's a bug worth reporting. The switch <paths.state>/gate-off turns the hook off (passes are still logged); it's
yours to use, not Anna's. Security model § Destructive and outward actions.
The night runs¶
No morning report¶
Check the timer ran: systemctl --user status anna-morning and journalctl --user -u anna-morning -n 50. The last
line of a run is … morning exit=<rc> -> journal/handoffs/<date>-morning.md. Common causes:
no record at …:paths.recordpoints to a folder that doesn't exist.pull failed: the record has a remote, andgit pull --ff-onlyfailed (diverged history, network, credentials). Rungit -C ~/anna-record pull --ff-onlyyourself and fix what it says.- No line at all: lingering is off and you weren't logged in.
The report's first line ends in exit 1 (or another nonzero code)¶
The headless session failed. The report file holds its output: read it. A login problem shows up here: run claude -p
"say hi" in a terminal to check your login, or make a token (Install § token).
With the bot on, you also got ⛔ anna-run morning failed (exit …) on Telegram.
The report says it couldn't run a command¶
Headless runs are deny-by-default: only the commands in ~/.claude/anna-headless-settings.json run. A check you want in
the morning report belongs in anna-checks as a section (<record>/checks/<name>), not as a new allowlist entry.
Checks and senses.
evening: run did not exit 0, drafts kept, or ⚠ evening fold left N session draft(s) unfolded; retried tomorrow¶
The fold didn't finish or didn't fold everything. Nothing is lost: the drafts stay in <paths.state>/drafts/ and the
next night tries again. The report journal/handoffs/<date>-evening.md says why.
evening: no drafts, no stream, no run is not an error: there was nothing to fold.
Checks and the clock¶
⛔ clock: ping/run items past due and not fired (anna-clock.timer?)¶
The clock tick isn't running. systemctl --user status anna-clock.timer should be active; systemctl --user enable --now
anna-clock.timer if not. After downtime it catches up on the next tick.
⚠ <name>: check failed to run (exit N): …¶
Your check in <record>/checks/<name> errored before printing an OK/⚠/⛔ line. Run it by hand
(~/anna-record/checks/<name>) to see why. A check that can't run is never reported as OK.
⚠ <name>: no such section, ⚠ checks: … has a built-in's name …¶
The section doesn't exist or isn't executable (chmod +x), or your check is named relay, clock or disk, which are
built in and would always win: rename it.
anna-clock: --ping needs a time, anna-clock: … is in the past, anna-clock: can't read WHEN¶
--ping and --run items need a time of day ("fri 09:00", "+2h"); plain notes can be a date alone. WHEN is anything
date -d understands, plus +3d, +2h, +90min. Commands.
The bot¶
anna-tg: no bot token at …/telegram-ops¶
The token file is missing or empty. Create it as in Telegram § Save the token.
anna-notify: no DM found; message the bot first¶
Send your bot any message in Telegram, then run anna-notify --learn again. If the bot is already running, it's
reading the messages instead: systemctl --user stop anna-tg@ops, DM the bot, anna-notify --learn, start it again.
anna-notify: no chat id yet (anna-notify --learn), anna-notify: send failed¶
The first: run anna-notify --learn. The second: check the token with anna-notify --me; {"ok":true,…} means it's
fine, and the chat ID in <paths.state>/telegram-ops.chat may be wrong (learn it again).
anna-notify: the bot is off (bot.enabled = false in your config)¶
Set bot.enabled = true in your config and rerun ./install.sh. With the bot off, alerts go to the stream only.
The bot doesn't answer¶
systemctl --user status anna-tg@ops: is it running?screen -r anna-tg-ops: is the session waiting on a question (folder trust, login)? Answer it.- Did you get a pairing code back? Then you're not on its allowlist yet: Telegram § allow only you.
- Is the plugin installed?
claude plugin listshould listtelegram@claude-plugins-official(withStatus: ✘ disabledat user scope: that's right, the bot's own settings turn it on).
Every Claude Code session tries to start the bot¶
The telegram or fakechat plugin is enabled at user scope. jq .enabledPlugins ~/.claude/settings.json should show
false for both; set them to false. Only the bot's and the brain's own settings files turn them on.
The relay¶
anna-relay status shows "brain":"offline" or "bot":"offline"¶
- Between
day.closeandday.openthe brain is expected to be offline (or"paused":truewhile it wraps up). Requests wait in the queue. - Otherwise, the session on that side wasn't started with the relay's channel. Restart it: the bot with
systemctl --user restart anna-tg@ops; the brain by typing/exitin it: it resumes after 10 s and reads the config again, relay included. offline (orphaned fakechat): a leftover channel server holds the port. The launchers clear it on the next start, and the bridge clears it too.
anna-checks relay sums it up: ⛔ relay: bridge … means the service isn't running (systemctl --user status
anna-relay); ⛔ relay: bot side … means hand-offs will stall.
⚠ anna-brain: fakechat port held by a live session; relay to … won't work¶
Another live Claude Code session holds the brain's port (relay.brain_port, default 8791), usually a second brain
window or a session started with the fakechat plugin enabled. Close it and restart the brain.
⚠ relay <id>: no result from the brain after N min¶
The brain got the request but hasn't answered (relay.alarm_min, default 30). Attach to the brain and look: it may be
waiting on a confirmation or still working. anna-relay list shows what's open.
⚠ relay: more than 5 hand-offs from the bot in 2 min; intake stopped (loop?)¶
The breaker tripped, usually a loop between the bot and the bridge. Look at anna-relay list and the bot's screen, then
systemctl --user restart anna-relay.
How the relay works and why these guards exist: The relay.