Skip to main content

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:
1

Check if Docker is running

docker info
If this fails, Docker is not running.
2

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
3

Verify permissions (Linux)

# Add user to docker group
sudo usermod -aG docker $USER
newgrp docker

# Test access
docker ps
4

Check container runtime binary

Edit src/container-runtime.ts to match your runtime:
export const CONTAINER_RUNTIME_BIN = 'docker';  // or 'podman'
Rebuild:
npm run build

Claude Code Process Exited with Code 1

Symptom: Agent spawns but immediately exits:
Claude Code process exited with code 1
Common Causes:
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
Cause: .env file missing or not mountedSolution:
  1. Verify .env exists in project root:
    cat .env
    
  2. Check data/env/env was created (extracted auth vars):
    cat data/env/env
    
  3. Restart service to remount:
    # macOS
    launchctl kickstart -k gui/$(id -u)/com.nanoclaw
    
    # Linux
    systemctl --user restart nanoclaw
    
Cause: nanoclaw-agent:latest image doesn’t existSolution:
# Check if image exists
docker images | grep nanoclaw-agent

# Build image
./container/build.sh
Cause: OAuth token expired or invalidSolution: Re-authenticate outside container:
claude auth login
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

1

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
2

Check logs for channel connection

grep -E 'Channel.*connected|Connecting' logs/nanoclaw.log | tail -10
Should see: WhatsApp channel connected or similar.
3

Verify group registration

sqlite3 store/messages.db "SELECT name, jid, trigger FROM registered_groups;"
Your chat must be in this table.
4

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)
5

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
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

Performance Issues

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:
npm run build

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

Collect Debug Information

#!/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
Node.js 20+ is in PATH
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

Build docs developers (and LLMs) love