Container Runtime Issues
Container Runtime Failed to Start
Symptom : Service starts but immediately exits with:
FATAL: Container runtime is required but failed to start
Cause : Docker/container runtime not running or not accessible.
Solutions :
Check if Docker is running
If this fails, Docker is not running.
Start Docker
macOS :open /Applications/Docker.app
# Or for Apple Container:
# No daemon - starts automatically with first container
Linux :sudo systemctl start docker
# Or Docker Desktop:
systemctl --user start docker-desktop
Verify permissions (Linux)
# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker
# Test access
docker ps
Check container runtime binary
Edit src/container-runtime.ts to match your runtime: export const CONTAINER_RUNTIME_BIN = 'docker' ; // or 'podman'
Rebuild:
Claude Code Process Exited with Code 1
Symptom : Agent spawns but immediately exits:
Claude Code process exited with code 1
Common Causes :
Session mount path incorrect
Cause : Sessions mounted to /root/.claude/ instead of /home/node/.claude/Solution : Container runs as node user (uid 1000), not root. Check mount in src/container-runner.ts:// Correct:
mounts . push ({
hostPath: sessionDir ,
containerPath: '/home/node/.claude/' , // ✓ Correct
readonly: false ,
});
// Incorrect:
// containerPath: '/root/.claude/', // ✗ Wrong user
Missing authentication credentials
Cause : .env file missing or not mountedSolution :
Verify .env exists in project root:
Check data/env/env was created (extracted auth vars):
Restart service to remount:
# macOS
launchctl kickstart -k gui/ $( id -u ) /com.nanoclaw
# Linux
systemctl --user restart nanoclaw
Container image not built
Cause : nanoclaw-agent:latest image doesn’t existSolution :# Check if image exists
docker images | grep nanoclaw-agent
# Build image
./container/build.sh
Claude Code CLI not authenticated
Cause : OAuth token expired or invalidSolution : Re-authenticate outside container:Then extract new token from ~/.claude/.credentials.json and update .env.
Containers Always Exit with Code 137 (SIGKILL)
Symptom : All containers killed abruptly instead of graceful shutdown
Cause : Idle timeout and container timeout are the same, so both timers fire simultaneously.
Solution : Make idle timeout shorter than container timeout:
# In .env or systemd service file:
IDLE_TIMEOUT = 300000 # 5 min
CONTAINER_TIMEOUT = 1800000 # 30 min
Containers wind down between messages, while the safety timeout catches stuck agents.
Service Issues
Service Not Starting
macOS (launchd) :
# Check service status
launchctl list | grep nanoclaw
# View error from system log
log show --predicate 'subsystem == "com.apple.launchd"' --last 5m | grep nanoclaw
# Check plist syntax
plutil -lint ~/Library/LaunchAgents/com.nanoclaw.plist
# View service logs
tail -f logs/nanoclaw.error.log
Linux (systemd) :
# Check service status
systemctl --user status nanoclaw
# View recent logs
journalctl --user -u nanoclaw -n 50
# Check for failed units
systemctl --user list-units --failed
# Verify service file syntax
systemd-analyze verify ~/.config/systemd/user/nanoclaw.service
Common Issues :
Incorrect paths : Use absolute paths, not ~ in plist/service files
Node not found : Add Node.js path to PATH environment variable
Permission denied : Check file permissions on project directory
Missing logs directory : mkdir -p logs/ in project root
Service Running but No Response to Messages
Check if service is actually running
macOS :launchctl list | grep nanoclaw
ps aux | grep node | grep nanoclaw
Linux :systemctl --user is-active nanoclaw
ps aux | grep node | grep nanoclaw
Check logs for channel connection
grep -E 'Channel.*connected|Connecting' logs/nanoclaw.log | tail -10
Should see: WhatsApp channel connected or similar.
Verify group registration
sqlite3 store/messages.db "SELECT name, jid, trigger FROM registered_groups;"
Your chat must be in this table.
Check trigger word
Messages must start with trigger word (default @Andy): # Check configured trigger
grep ASSISTANT_NAME logs/nanoclaw.log | head -1
Try: @Andy hello (trigger at start, case insensitive)
Check message polling
grep -E 'Processing messages|No messages to process' logs/nanoclaw.log | tail -10
Should poll every 2 seconds.
Session and Memory Issues
Session Not Continuing
Symptom : Agent forgets previous conversation every time
Causes and Solutions :
Check :sqlite3 store/messages.db "SELECT group_folder, session_id FROM sessions;"
If empty or NULL, sessions aren’t being saved. Debug : Check container logs for session ID in output:cat groups/main/logs/container- * .log | grep sessionId
Check : Container user is node with HOME=/home/nodeSessions must mount to /home/node/.claude/, not /root/.claude/ Verify mount :docker run -it --rm nanoclaw-agent:latest sh -c 'echo $HOME'
# Should output: /home/node
Session directory not writable
Check permissions :ls -la data/sessions/
ls -la data/sessions/main/.claude/
Fix :chmod -R 755 data/sessions/
Memory (CLAUDE.md) Not Loading
Check that files exist :
ls -la groups/CLAUDE.md # Global memory
ls -la groups/main/CLAUDE.md # Group memory
Verify working directory mount :
Agent cwd is /workspace/group, which is mounted from groups/{name}/:
# Test inside container:
docker run -it --rm \
-v "$( pwd )/groups/main:/workspace/group" \
nanoclaw-agent:latest sh -c 'ls -la /workspace/group/'
Should see CLAUDE.md and other group files.
Claude Agent SDK loads :
../CLAUDE.md (parent = global)
./CLAUDE.md (current = group)
Both must be readable from container’s perspective.
Database Issues
Messages Not Being Stored
# Check if database exists
ls -lh store/messages.db
# Check tables
sqlite3 store/messages.db ".tables"
# View recent messages
sqlite3 store/messages.db "SELECT id, chat_jid, substr(body, 1, 50), created_at FROM messages ORDER BY created_at DESC LIMIT 10;"
# Check database locks
lsof | grep messages.db
If database is locked :
Stop service
Check for orphaned processes: ps aux | grep node
Delete lock file: rm store/messages.db-shm store/messages.db-wal
Restart service
Database Migration Failed
Symptom : Service crashes on startup with SQLite errors
Solution : Delete database and let it reinitialize (loses history):
# Backup first
cp store/messages.db store/messages.db.backup
# Remove database
rm store/messages.db
# Restart service - will create fresh DB
Channel-Specific Issues
WhatsApp: QR Code Expired
Symptom : Logs show “Connection closed” or “Session expired”
Solution :
# Stop service
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist # macOS
systemctl --user stop nanoclaw # Linux
# Delete auth state
rm -rf store/auth/
# Start service and scan new QR code
launchctl load ~/Library/LaunchAgents/com.nanoclaw.plist # macOS
systemctl --user start nanoclaw # Linux
# Watch logs for QR code
tail -f logs/nanoclaw.log | grep -A 20 'QR code'
Channel Missing: “No registered channels”
Cause : Channel skill not installed or credentials missing
Check installed channels :
ls src/channels/
# Should see: whatsapp.ts, telegram.ts, etc.
Check barrel import :
cat src/channels/index.ts
# Should import all channel files
Solution : Install channel skill:
# In Claude Code:
/add-whatsapp
/add-telegram
# etc.
Or apply manually:
npx tsx scripts/apply-skill.ts .claude/skills/add-whatsapp
npm run build
High Memory Usage
Check active containers :
docker ps --format '{{.Names}}\t{{.Status}}' | grep nanoclaw
docker stats --no-stream
Solutions :
Reduce MAX_CONCURRENT_CONTAINERS (default: 5)
Lower IDLE_TIMEOUT so containers exit faster
Check for stuck containers:
docker ps -a | grep nanoclaw
docker rm -f $( docker ps -aq -f name=nanoclaw- )
Messages Lost on Timeout
Symptom : Container times out, retries find no messages
Cause : processGroupMessages advances cursor (lastAgentTimestamp) before agent runs
Debug :
# Check if container timed out
grep -E 'timeout|SIGKILL|exit code 137' logs/nanoclaw.log
# Check cursor vs message timestamps
sqlite3 store/messages.db "
SELECT c.jid, c.last_agent_timestamp,
(SELECT created_at FROM messages WHERE chat_jid = c.jid ORDER BY created_at DESC LIMIT 1) AS last_message
FROM chats c;
"
Workaround : Manually reset cursor:
sqlite3 store/messages.db "
UPDATE chats
SET last_agent_timestamp = (SELECT created_at FROM messages WHERE chat_jid = chats.jid ORDER BY created_at LIMIT 1)
WHERE jid = 'YOUR_CHAT_JID';
"
Log Inspection
Finding Relevant Logs
Service logs :
# macOS
tail -f logs/nanoclaw.log
tail -f logs/nanoclaw.error.log
# Linux (systemd)
journalctl --user -u nanoclaw -f
Container logs (per-agent execution):
# Find most recent container logs
ls -lt groups/ * /logs/container- * .log | head -10
# Read specific group's logs
tail -f groups/main/logs/container- $( ls -t groups/main/logs/ | head -1 )
# Search all container logs
grep -r "error" groups/ * /logs/container- * .log
Useful log patterns :
# Agent spawns
grep -E 'Spawning container|Starting container' logs/nanoclaw.log
# Container exits
grep -E 'Container.*exited|process exited' logs/nanoclaw.log
# Message processing
grep -E 'Processing messages|catchUpMessages' logs/nanoclaw.log
# Channel connections
grep -E 'Channel.*connect|connected' logs/nanoclaw.log
# Errors
grep -i 'error\|fatal\|fail' logs/nanoclaw.log
Debug Mode
Run manually with verbose logging:
# Stop service first
launchctl unload ~/Library/LaunchAgents/com.nanoclaw.plist # macOS
systemctl --user stop nanoclaw # Linux
# Run manually
cd ~/nanoclaw-pro
DEBUG = * node dist/index.js
Press Ctrl+C to stop. Restart service when done.
Mount Security Issues
Additional Mount Rejected
Symptom : Container spawn fails with “Mount path not allowed”
Cause : Path not in allowlist in src/mount-security.ts
Check group’s container config :
sqlite3 store/messages.db "
SELECT name, container_config
FROM registered_groups
WHERE container_config IS NOT NULL;
"
Solution : Add path to allowlist or fix the mount path:
Edit src/mount-security.ts:
const ALLOWED_PREFIXES = [
homeDir ,
'/tmp' ,
'/var/tmp' ,
'/path/to/your/directory' , // Add custom paths
];
Rebuild:
Read-Only Mount Not Working
Symptom : Agent can write to supposedly read-only mount
Cause : :ro suffix may not work on all runtimes
Check mount implementation :
In src/container-runtime.ts:
export function readonlyMountArgs (
hostPath : string ,
containerPath : string ,
) : string [] {
// Use --mount for true readonly
return [
'--mount' ,
`type=bind,source= ${ hostPath } ,target= ${ containerPath } ,readonly` ,
];
}
Getting Help
#!/bin/bash
# Save to debug-info.sh
echo "=== System Info ==="
uname -a
node --version
docker --version
echo "\n=== Service Status ==="
# macOS:
launchctl list | grep nanoclaw
# Linux:
# systemctl --user status nanoclaw
echo "\n=== Container Runtime ==="
docker info
docker ps -a | grep nanoclaw
echo "\n=== Recent Logs ==="
tail -50 logs/nanoclaw.log
tail -50 logs/nanoclaw.error.log
echo "\n=== Database State ==="
sqlite3 store/messages.db "SELECT name, jid FROM registered_groups;"
sqlite3 store/messages.db "SELECT group_folder, session_id FROM sessions;"
echo "\n=== Container Image ==="
docker images | grep nanoclaw
echo "\n=== Recent Container Logs ==="
ls -lt groups/ * /logs/container- * .log | head -5
Run: bash debug-info.sh > debug-output.txt
Include debug-output.txt when asking for help.
Common Mistake Checklist
Container runtime (Docker) is running
Container image nanoclaw-agent:latest exists
.env file exists with valid credentials
Service file uses absolute paths (no ~)
Group is registered in SQLite
Message starts with trigger word (@Andy)
Logs directory exists and is writable
Build tools installed (for better-sqlite3)
Session mounts to /home/node/.claude/ not /root/.claude/
Next Steps
macOS Deployment Back to macOS setup guide
Linux Deployment Back to Linux setup guide