Troubleshooting
401 or “token expired”
The access token was refreshed and rejected, or the refresh token was rotated by another machine. Run tf auth once on the machine you want to keep, click Authorize, and stop the other host’s cron.
No .tokens.json means auth has not succeeded in this directory. Check --dir and the working directory.
Callback problems
Timed out waiting for authorization (5 min) — the browser never hit the listener. Re-run tf auth. Confirm the X app callback is exactly http://127.0.0.1:8675/callback unless you overrode the port and redirect URI.
State mismatch — the callback was not for this attempt. Re-run auth; don’t reuse an old URL.
Token exchange failed — the client id, secret, or redirect URI doesn’t match the app. The redirect URI in the exchange is the one the CLI printed (Redirect URI in use).
Headless: tf auth --no-browser, open the URL elsewhere, and make sure that browser can reach the bind address.
403 on a source
The log says SKIP LIKES or similar. Bookmarks in particular require OAuth 2.0 user context and the bookmark.read scope. Re-check app permissions (Read) and re-run tf auth so the new scopes are granted.
Rate limit
A 429 sleeps until x-rate-limit-reset plus two seconds, then retries that page. Let it sit.
TAVILY_API_KEY not set
tf search, tf enrich, and tf cron need the key. tf cron fails at the enrich step even if fetch already succeeded. Use tf refresh when you only want a refresh. --timely on generate is best-effort: a missing key or a Tavily error is ignored and drafts still run.
claude CLI not found
Install Claude Code and log in, or set TF_LLM_PROVIDER to anthropic, openrouter, or openai and the matching key. A provider set without its key throws API key is not set.
Generate returned prose or invalid JSON
The model has to return a single JSON object matching the bundle. Zod throws otherwise. Re-run tf generate, or set TF_LLM_MODEL / --model to a model that follows the JSON instruction. --json shows the failure on stdout/stderr without the markdown file.
Notion pages missing
The integration can only see pages you shared with it. tf notion search is the check. sync logs unreachable and skips a page that was unshared later.
Slack did nothing
Unset SLACK_WEBHOOK_URL is a logged skip, not a crash. Cron only pings when the pending count is greater than 0. The first flush baselines to 0, so the first cron after a backfill often stays quiet. Fetch something new, run tf flush, then cron again.
tf onboard won’t open
The TUI uses OpenTUI’s native addon. Build with bun run build on the same OS you run (the release workflow compiles per target for this reason). A dumb terminal or a missing addon fails at launch. --selftest is a one-frame smoke check in an environment where the renderer can start.
Bun missing
./setup_x.sh installs it under $HOME/.bun/bin. Open a new shell or export that path. deploy.sh logs a warning and skips the rebuild if bun is absent.
Wrong project directory
Commands read whichever root they discovered. If counts look empty, you are probably not in the checkout that holds tweets.db. Pass --dir or cd to the directory that contains .env.
Persona still sounds generic
persona/PERSONA.md is missing, still the template (@YOUR_HANDLE), or very thin. tf analyze shows what the model can see from the database. Fill the voice rules and banned phrases, then generate again.
Verify. After a fix, re-run the failing command once.
./setup_x.sh --checkshould list the required OAuth keys as present and Bun as installed.