Practical diagnostics for the most common problems. Each entry has the symptom you see, the cause, and a fix you can copy-paste.
Convention: all commands assume you are inside the cloned repo directory on the host running Docker Compose.
Before deep diving, run this:
docker compose ps
docker compose logs --tail 30 bot opencode | head -120Both services should show Up. Anything reporting Restarting or
Exited is the first thing to chase.
Symptom: you message the bot, no reply, no error.
Possible causes & fixes:
-
Wrong
TELEGRAM_ALLOWED_USER_ID. The bot silently ignores anyone else. Get your real ID from @userinfobot and compare against.env. Then:docker compose logs bot 2>&1 | grep -i "Unauthorized access"
shows attempts from IDs that didn't match.
-
Bot token revoked or wrong. Recreate via @BotFather, paste into
.env, thendocker compose restart bot. -
Telegram polling stuck. Restart:
docker compose restart bot
Symptom: bot logs show [Bot] Error fetching projects: TypeError: fetch failed,
or in Telegram OpenCode server is not available.
Causes:
-
OpenCode container is restarting. Check:
docker compose ps
If
opencodeshowsRestarting (1), dig into its logs:docker compose logs --tail 80 opencode
Common errors and their fixes are in the next section.
-
Network configuration.
OPENCODE_API_URLshould behttp://opencode:4096(compose-network internal address). If you changed it tohttp://localhost:4096, the bot container can't reach the host's localhost — restore the default. -
OpenCode just took a long time to start. First boot includes a one-time DB migration that takes ~30 s. Watch the log; when you see
opencode server listening on http://0.0.0.0:4096, retry.
Symptom:
[+] Running 1/1
✘ opencode Error error from registry: denied
Error response from daemon: error from registry: denied
Cause: an outdated install referenced ghcr.io/sst/opencode:latest,
which is not a published image. The current Dockerfile.opencode
builds OpenCode locally from the npm package.
Fix:
git pull
docker compose up -d --buildIf you forked or vendored the repo, make sure docker-compose.yml
references build: { dockerfile: Dockerfile.opencode } for the
opencode service, not a registry image.
Symptom:
opencode-1 | spawnSync /usr/local/lib/node_modules/opencode-ai/bin/.opencode ENOENT
repeating every few seconds; opencode container shows Restarting (1).
Cause: Dockerfile.opencode was built on Alpine. The
opencode-ai npm package ships platform-specific binaries via
optionalDependencies; the published binaries link against glibc, so
on musl (Alpine) none match and the wrapper has nothing to spawn.
Fix: the project ships a glibc-based image (node:22-slim) since
this was identified. Make sure your local Dockerfile.opencode
starts with FROM node:22-slim (not :alpine), then:
git pull
docker compose down
docker compose up -d --buildSymptom: bot logs show
Error: Could not locate the bindings file. Tried:
→ /app/node_modules/better-sqlite3/build/Release/better_sqlite3.node
...
…and the SQLite migration fails on bot startup.
Cause: the bot Dockerfile previously used npm ci --ignore-scripts,
which skips the postinstall step that builds better-sqlite3's native
.node binding.
Fix: the multi-stage Dockerfile has been restructured to install
build tools (python3, build-essential) and run npm ci with
scripts enabled. After pulling the fix, rebuild:
git pull
docker compose down
docker compose up -d --buildIf you're hitting this in a custom build, do not pass
--ignore-scripts to npm install for production deps.
Symptom: in Telegram, the assistant says something like "I cannot
save permanently because the memory tools aren't available in this
session" or you see invalid tool errors.
Cause: OpenCode reads ~/.config/opencode/opencode.json (NOT
mcp.json, which is a different vendor's convention). The schema
must use a top-level mcp key with type: "remote" for HTTP-based
MCP servers.
Diagnose:
docker compose exec opencode cat /root/.config/opencode/opencode.jsonYou should see:
{
"mcp": {
"opencode-assistant-memory": {
"type": "remote",
"url": "http://bot:4097/mcp"
}
}
}Fix paths:
- If the file doesn't exist or is missing the
mcpkey, the entrypoint failed to write it. CheckASSISTANT_MEMORY_MCP_URLindocker-compose.yml. Remove the legacymcp.jsonif it exists:docker compose exec opencode rm -f /root/.config/opencode/mcp.json docker compose restart opencode - If the file is right but the assistant still doesn't call the
tools, prove the HTTP endpoint is reachable from inside the
opencode container:
Should return JSON listing 9 tools.
docker compose exec opencode \ curl -s -X POST http://bot:4097/mcp \ -H 'Content-Type: application/json' \ -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Symptom:
[App] Memory migration failed; continuing with markdown sources: <error>
Cause: the SQLite DB couldn't be created or opened. Most often a
permissions issue with the memory/ mount.
Fix:
- Verify the host directory is writable by the container:
ls -ld memory/
- If you're upgrading from an older install, the migration is
idempotent: if the DB exists with rows, it skips. Delete and let
it re-run:
mv memory/data.db memory/data.db.broken docker compose restart bot docker compose logs bot 2>&1 | grep -i "Memory migrated"
Symptom: you say "recuérdame que prefiero TypeScript", the bot saves it. In a new session, "qué lenguaje prefiero?" and the assistant says it doesn't know.
Causes & fixes:
- Old session. Each session has a cached system prompt. New facts
only land in new sessions. Use
/newto start a fresh one. - Fact older than the inline window. The first 20 facts get
inlined into the new-session system prompt automatically. Older
facts require the model to call
fact_search. If yours is older,/memory_search <keyword>from your side proves it's in the DB; if the model still doesn't pull it, ask explicitly: "check the memory tools and tell me the answer". - Duplicates accumulating.
addFactdeduplicates exact(content, category)matches. If category was set differently each time, you'll see multiple entries for the same content. Clean with/memory_search <substring>then/memory_remove <id>for the stale ones.
Symptom: you ran /skill_install <url> but /listskill does not
show the new skill.
Causes:
- Install failed silently. The
/skill_installcommand shows the slug it used; if you didn't get a confirmation, look at:docker compose logs bot 2>&1 | grep -i "skill_install"
- You dropped a
.mdfile intomemory/skills/directly. That path is no longer the source of truth. Skills live in SQLite. Use/skill_install <url>(or copy the file content and paste into a raw URL host).
To pull new bot code and rebuild:
git pull
docker compose down
docker compose up -d --buildIf git pull rejects because you've edited a tracked file (e.g.
memory/soul.md with your customization):
# 1. backup your customization
cp memory/soul.md memory/soul.md.mio.backup
# 2. discard local changes so pull works
git checkout memory/soul.md
# 3. pull
git pull
# 4. compare and re-apply your custom bits
diff memory/soul.md memory/soul.md.mio.backupThe startup hook resyncs memory/soul.md and memory/agents.md to
SQLite on every restart, so editing those files (and restarting) is
the supported customization path.
docker compose up -d --build opencode reconstructs the image — anything
you installed by shelling into the container goes back to whatever the
Dockerfile produces. What persists vs what doesn't:
| Where the install lands | Persists across --build? |
|---|---|
/root/.config/opencode/opencode.json (e.g. MCP server entries via jq) |
✅ Yes — opencode-config volume |
/root/.npm-global (any npm install -g <pkg>) |
✅ Yes — opencode-npm volume |
/root/.local (any pip install --user <pkg>) |
✅ Yes — opencode-pip volume |
/usr/local/bin/<binary> (e.g. apt-get install, pip install system-wide, curl ... -o /usr/local/bin/...) |
❌ No — these are in image layers |
/usr/local/lib/node_modules/opencode-ai |
--build |
So as long as you stick to:
docker compose exec opencode npm install -g <pkg>
docker compose exec opencode pip install --user <pkg>your installs survive every rebuild. If you instead did apt-get install
or a pip install without --user, that install will be gone after the
next rebuild — you'll need to repeat it, or put it in Dockerfile.opencode
permanently if it's something you want every user of this repo to have.
To verify what's installed and where:
# npm globals (user-installed) — should be in volume
docker compose exec opencode ls /root/.npm-global/bin
# pip --user binaries — also in volume
docker compose exec opencode ls /root/.local/bin
# OpenCode itself — system path, replaced on rebuild
docker compose exec opencode which opencode| Where | Command |
|---|---|
| Bot startup, MCP server, memory migration | docker compose logs --tail 100 bot |
| OpenCode startup, MCP discovery | docker compose logs --tail 100 opencode |
| Real-time tail of both | docker compose logs -f bot opencode |
| What's in the SQLite memory | /memfiles and /listskill in Telegram |
| Audit history of memory mutations | /memfiles (mentions audit log), or query audit_recent via the MCP HTTP endpoint |
If a problem is not on this list, open an issue with:
docker compose ps- Last 80 lines of relevant logs
- The exact Telegram message that triggered the bug