Skip to main content
When something goes wrong, start with the startup checks below, then look through the categorized issues. Most problems trace back to missing environment variables, stale PID files, or corrupt data files — all recoverable.
Before diving into manual troubleshooting, run ollim-bot doctor — it checks environment variables, data directory, timezone, routines, reminders, tool policy, state files, and Claude authentication in one pass. See the doctor CLI reference for details.

Startup failures

These are the most common blockers when launching ollim-bot.

Missing environment variables

main() validates all three required variables on startup. If any are missing, the bot shows all missing vars at once with hints and exits:
Missing required env vars in .env:
  DISCORD_TOKEN    (bot token from Discord Developer Portal)
  OLLIM_USER_NAME  (your first name — the bot will call you this)
  OLLIM_BOT_NAME   (your bot's name, e.g. Ollim)
Set the missing variables in your .env file (at the project root) and restart.

Invalid Discord token

When bot.start() receives a LoginFailure exception from discord.py, the bot prints an actionable error and exits with code 1:
Invalid Discord token. Check DISCORD_TOKEN in .env
Get a new token: Discord Developer Portal > Bot > Reset Token
Regenerate your token in the Discord Developer Portal and update DISCORD_TOKEN in .env.

Message Content Intent not enabled

When bot.start() raises PrivilegedIntentsRequired, the bot exits with code 1:
Message Content Intent is not enabled.
Enable it: Discord Developer Portal > Bot > Privileged Gateway Intents
The bot requires the Message Content Intent to read message text. The quickstart’s Discord bot setup shows where to find this toggle.

Discord API 401 during authentication

This is caught before bot.start() — during the Claude auth DM flow, when the bot uses the Discord REST API directly. If the API returns a 401:
Discord API returned 401 — DISCORD_TOKEN appears invalid.
Check that DISCORD_TOKEN in .env matches the token from
Discord Developer Portal > Bot > Reset Token.
Exits with code 1. This catches the same underlying issue as Invalid Discord token (a bad token) but earlier in the startup sequence, before the bot fully connects.

Bot already running

The bot writes its PID to ~/.ollim-bot/state/bot.pid on startup and checks whether that process is still alive:
  • Primary check: os.kill(pid, 0) — works on all platforms
  • Linux enhancement: additionally reads /proc/<pid>/cmdline to verify the process is actually ollim-bot, preventing false positives from recycled PIDs
If a running instance is detected:
ollim-bot is already running (pid 12345)
Corrupted PID files — non-numeric content or the file disappearing mid-read — are handled gracefully and overwritten on the next startup. If the previous process crashed without cleaning up the PID file, delete it manually:
rm ~/.ollim-bot/state/bot.pid
The file is normally deleted on exit via atexit.

No owner found

If bot.application_info() returns no owner, the bot prints:
warning: no owner found; scheduler and DM disabled
The bot stays online but cannot send DMs, run scheduled routines, or start the webhook server. The owner is automatically the Discord account that created the application — this error usually means the application was created by a team with no members.

Discord API outage (503)

Transient Discord API outages return a DiscordServerError (HTTP 503). The bot retries automatically up to 5 times with exponential backoff — 5s, 10s, 20s, 40s, 80s between attempts. During retries, the console shows:
Discord API error (503 Service Unavailable) — retrying in 10s (attempt 2/5)
If all 5 retries fail, the bot gives up and exits with code 1:
Discord API error (503 Service Unavailable) — giving up after 5 retries.
Check https://discordstatus.com for outages.
If you see persistent 503 errors, check discordstatus.com for ongoing incidents. With Restart=on-failure in a systemd service, the bot restarts and tries again automatically.

Unexpected shutdown

The bot sends a DM before exiting on crash, SIGINT, or SIGTERM:
shutting down: received SIGTERM
Or for crashes:
shutting down: ConnectionError: gateway disconnected
The notification is best-effort — if the Discord connection is already down, the DM is silently skipped. If you’re running as a systemd service with Restart=on-failure, the bot comes back automatically after unexpected exits.

Claude auth not completing

On startup, the bot checks Claude authentication via the bundled CLI. If not logged in, it DMs you an OAuth link and waits for you to complete sign-in. If you don’t receive the DM, verify your DISCORD_TOKEN is correct — the bot uses the Discord REST API directly (not discord.py) to send the auth DM before fully connecting. If the DM arrives but login fails after clicking the link, run ollim-bot auth login manually to see the CLI output.
# Check current auth status
ollim-bot auth status

# Manually trigger the login flow
ollim-bot auth login
If authentication fails repeatedly, verify your Claude subscription is active and that the bundled CLI at claude_agent_sdk/_bundled/ is intact — reinstalling with uv tool install --editable . rebuilds it.

Session issues

The bot reads ~/.ollim-bot/state/sessions.json on startup. If the file is missing or empty, it starts a fresh session. Check that the file exists and contains a session ID string:
cat ~/.ollim-bot/state/sessions.json
If the file contains a JSON object (starts with {), load_session_id() returns None and the bot starts fresh. This can happen if another tool writes JSON to the file. Replace it with just the session ID string.
The Agent SDK auto-compacts conversations when they grow too long. Each compaction produces a new session ID and a compacted event in session_history.jsonl. This is normal behavior — the bot detects the ID change in save_session_id() and logs it automatically.If compaction happens too frequently, the conversation is growing fast. Use /clear to start fresh, or use /compact to trigger a manual compaction at a natural breakpoint.
The append-only log at ~/.ollim-bot/state/session_history.jsonl records every lifecycle event. To inspect it:
cat ~/.ollim-bot/state/session_history.jsonl
Each line is a JSON object with session_id, event, timestamp, and parent_session_id. Use this to trace session transitions, find the last known good session ID, or understand what happened before a crash.To manually resume a specific session, write its ID to sessions.json:
echo -n "sess_abc123" > ~/.ollim-bot/state/sessions.json

Data directory issues

All persistent state lives in ~/.ollim-bot/. See the data directory reference for the full layout.
read_md_dir() catches ValueError, yaml.YAMLError, TypeError, and KeyError when parsing .md files. Corrupt files are logged as warnings and skipped — they do not crash the bot.To find corrupt files, check the bot’s log output for warnings, then inspect or delete the offending file from ~/.ollim-bot/routines/, reminders/, or webhooks/.
read_jsonl() skips blank lines and lines that do not start with {. Lines that start with { but contain invalid JSON are logged as warnings and skipped — they do not crash the read. It also filters fields to only those known to the target dataclass, providing forward compatibility.If a JSONL file is entirely corrupted, delete it. The bot recreates these files on the next write:
rm ~/.ollim-bot/state/session_history.jsonl
All writes use atomic temp-file-then-rename (tempfile.mkstemp + os.replace). Manual edits to files in ~/.ollim-bot/ while the bot is running risk being overwritten or causing inconsistent state. Stop the bot first, make edits, then restart.
The ~/.ollim-bot/ directory is a git repository. If you manually edit files, the bot’s auto-commit on next write will include your changes in its commit. Use git log inside the directory to review the change history.

Google OAuth issues

The Google integration requires ~/.ollim-bot/state/credentials.json from the Google Cloud Console. Without it, all Google-related commands (tasks, cal, gmail) and agent tools fail. Follow the Google integration setup guide to create and place this file.
~/.ollim-bot/state/token.json is auto-generated after /google-auth and refreshed automatically. If the token is revoked (password change, manual revocation), the bot detects it automatically and sends you a message to reconnect. Run /google-auth to re-authenticate.If the automatic detection does not trigger (e.g., the bot was offline when the revocation happened), you can delete the token manually and reconnect:
rm ~/.ollim-bot/state/token.json
Then run /google-auth in Discord.

Scheduling issues

The scheduler polls the routines/ and reminders/ directories every 10 seconds and registers APScheduler jobs. Check:
  1. The bot printed scheduler started: N jobs on startup (visible in the console).
  2. The .md file has valid YAML frontmatter with a cron (routines) or run-at (reminders) field.
  3. The id field in the YAML is unique — duplicate IDs cause one to overwrite the other.
  4. The owner was found on startup (no "warning: no owner found" message). The scheduler does not start without an owner.
Background forks that exceed the ping budget get tool errors from ping_user and discord_embed. The user is not notified — the tool returns an error to the agent, which decides how to proceed.Check the budget state:
cat ~/.ollim-bot/state/ping_budget.json
The available field shows remaining pings. The bucket refills 1 ping per 90 minutes, capped at a capacity of 5. Use /ping-budget in Discord to view a formatted status. See ping budget for details.

Background fork issues

Background fork client initialization can fail with a “Control request timeout” error from the Claude Agent SDK. The bot automatically retries up to 3 times with 5s and 15s backoff delays.If all 3 attempts fail, the error is logged and the bot sends you a DM that the background task failed. This is typically a transient issue — the next scheduled fire usually succeeds. If it persists, check your network connection and the Claude API status.
When a background fork crashes or exceeds its timeout (bg_fork_timeout, default 30 minutes), the bot sends a best-effort DM notification:
Background task timed out after 30 minutes: `[routine:daily-email-check]`
Or for crashes:
Background task failed: `[reminder:workout-nudge]` -- check logs for details.
These messages are informational — the fork has already been cleaned up. If the task was a routine, the next scheduled fire will try again automatically. If it was a one-shot reminder, you may need to reschedule it.

Fork issues

Replying to a background fork message starts an interactive fork that resumes from that fork’s session. This relies on ~/.ollim-bot/state/fork_messages.json, which maps Discord message IDs to fork session IDs.Records older than 7 days are pruned on every read. If the background fork message is older than 7 days, the mapping no longer exists and the reply is treated as a normal message.
Interactive forks have an idle timeout. The scheduler checks every 60 seconds — after idle_timeout minutes of inactivity, it prompts the agent to exit. After another timeout period, it escalates with a stronger prompt. The agent always decides the exit strategy (report_updates, exit_fork, or save_context).If the fork is truly stuck, use the exit buttons on the fork embed, or run /clear to reset the entire session.

Permission issues

The default permission mode is dontAsk. In this mode, non-whitelisted tools are silently denied without prompting the user in Discord.Switch to a more permissive mode with the /permissions command:
ModeBehavior
dontAskNon-whitelisted tools silently denied (default)
defaultNon-whitelisted tools trigger Discord approval prompt
acceptEditsFile edits auto-approved, others prompt
bypassPermissionsAll tools approved
The _session_allowed set persists across interactive forks but resets on /clear.
In default or acceptEdits mode, the bot sends a Discord message with reaction-based approval. The prompt has a 60-second timeout — if you miss it, the tool is auto-denied. Check that you are in a mode that sends prompts (default or acceptEdits, not dontAsk).

Windows platform issues

Windows defaults to the system codepage (e.g., cp1252) which can corrupt Unicode characters in CLI output. The bot forces UTF-8 encoding on stdout and stderr before running any subcommand, so all CLI paths (ollim-bot, ollim-bot routine, ollim-bot reminder, etc.) produce correct output.If you still see garbled text, verify your terminal supports UTF-8. In PowerShell, run chcp 65001 to switch to UTF-8 manually.
When installed via uv tool install, the entry point on Windows is an .exe file — not a Python script. The auto-updater uses sys.argv directly to restart, which handles both .exe entry points and Python script invocations correctly. If the bot fails to restart after an update, verify that ollim-bot is on your PATH and was installed with uv tool install --editable ..

Diagnostic files

Quick reference for where to look when debugging. Run ollim-bot doctor first — it validates most of these files automatically and reports PASS, WARN, or FAIL for each.
FileWhat it tells you
~/.ollim-bot/state/sessions.jsonCurrent session ID (or missing = no active session)
~/.ollim-bot/state/session_history.jsonlTimeline of session lifecycle events
~/.ollim-bot/state/bot.pidPID of running bot instance
~/.ollim-bot/state/ping_budget.jsonCurrent ping budget state and counters
~/.ollim-bot/state/pending_updates.jsonQueued background fork summaries
~/.ollim-bot/state/fork_messages.jsonDiscord message to fork session mappings
~/.ollim-bot/state/inquiries.jsonActive button inquiry prompts
The ~/.ollim-bot/ directory is git-tracked. Run git log --oneline inside it to see a chronological record of all data changes.

Next steps

Data directory

Full layout of ~/.ollim-bot/ and file format details.

Session management

How sessions persist, compact, and recover across restarts.

Development guide

Dev setup, running locally, and code conventions.

Configuration reference

All environment variables and their defaults.