Fix orphan container cleanup and update installation steps (#149)
Updated instructions for handling orphaned containers and Zod version conflicts. Added critical cleanup steps for the NanoClaw service.
This commit is contained in:
@@ -40,10 +40,10 @@ Read `package.json` and add the `openai` package to dependencies:
|
||||
}
|
||||
```
|
||||
|
||||
Then install it:
|
||||
Then install it. **IMPORTANT:** The OpenAI SDK requires Zod v3 as an optional peer dependency, but NanoClaw uses Zod v4. This conflict is guaranteed, so always use `--legacy-peer-deps`:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm install --legacy-peer-deps
|
||||
```
|
||||
|
||||
### Step 2: Create Transcription Configuration
|
||||
@@ -296,41 +296,83 @@ if (registeredGroups[chatJid]) {
|
||||
}
|
||||
```
|
||||
|
||||
### Step 6: Update Package Lock and Build
|
||||
### Step 6: Fix Orphan Container Cleanup (CRITICAL)
|
||||
|
||||
Run these commands to ensure everything compiles:
|
||||
**This step is essential.** When the NanoClaw service restarts (e.g., `launchctl kickstart -k`), the running container is detached but NOT killed. The new service instance spawns a fresh container, but the orphan keeps running and shares the same IPC mount directory. Both containers race to read IPC input files, causing the new container to randomly miss messages — making it appear like the agent doesn't respond.
|
||||
|
||||
The existing cleanup code in `ensureContainerSystemRunning()` in `src/index.ts` uses `container ls --format {{.Names}}` which **silently fails** on Apple Container (only `json` and `table` are valid format options). The catch block swallows the error, so orphans are never cleaned up.
|
||||
|
||||
Find the orphan cleanup block in `ensureContainerSystemRunning()` (the section starting with `// Kill and clean up orphaned NanoClaw containers from previous runs`) and replace it with:
|
||||
|
||||
```typescript
|
||||
// Kill and clean up orphaned NanoClaw containers from previous runs
|
||||
try {
|
||||
const listJson = execSync('container ls -a --format json', {
|
||||
stdio: ['pipe', 'pipe', 'pipe'],
|
||||
encoding: 'utf-8',
|
||||
});
|
||||
const containers = JSON.parse(listJson) as Array<{ configuration: { id: string }; status: string }>;
|
||||
const nanoclawContainers = containers.filter(
|
||||
(c) => c.configuration.id.startsWith('nanoclaw-'),
|
||||
);
|
||||
const running = nanoclawContainers
|
||||
.filter((c) => c.status === 'running')
|
||||
.map((c) => c.configuration.id);
|
||||
if (running.length > 0) {
|
||||
execSync(`container stop ${running.join(' ')}`, { stdio: 'pipe' });
|
||||
logger.info({ count: running.length }, 'Stopped orphaned containers');
|
||||
}
|
||||
const allNames = nanoclawContainers.map((c) => c.configuration.id);
|
||||
if (allNames.length > 0) {
|
||||
execSync(`container rm ${allNames.join(' ')}`, { stdio: 'pipe' });
|
||||
logger.info({ count: allNames.length }, 'Cleaned up stopped containers');
|
||||
}
|
||||
} catch {
|
||||
// No containers or cleanup not supported
|
||||
}
|
||||
```
|
||||
|
||||
### Step 7: Build and Restart
|
||||
|
||||
```bash
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
|
||||
If using `--legacy-peer-deps` (due to Zod version conflicts), use:
|
||||
Before restarting the service, kill any orphaned containers manually to ensure a clean slate:
|
||||
|
||||
```bash
|
||||
npm install --legacy-peer-deps
|
||||
npm run build
|
||||
container ls -a --format json | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
nc = [c['configuration']['id'] for c in data if c['configuration']['id'].startswith('nanoclaw-')]
|
||||
if nc: print(' '.join(nc))
|
||||
" | xargs -r container stop 2>/dev/null
|
||||
container ls -a --format json | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
nc = [c['configuration']['id'] for c in data if c['configuration']['id'].startswith('nanoclaw-')]
|
||||
if nc: print(' '.join(nc))
|
||||
" | xargs -r container rm 2>/dev/null
|
||||
echo "Orphaned containers cleaned"
|
||||
```
|
||||
|
||||
### Step 7: Restart NanoClaw
|
||||
|
||||
Restart the service to load the new transcription code:
|
||||
Now restart the service:
|
||||
|
||||
```bash
|
||||
# If using launchd (macOS):
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
||||
|
||||
# Or if running manually:
|
||||
# Stop the current process and restart with:
|
||||
npm start
|
||||
```
|
||||
|
||||
Verify it started:
|
||||
Verify it started with exactly one (or zero, before first message) nanoclaw container:
|
||||
|
||||
```bash
|
||||
sleep 2 && launchctl list | grep nanoclaw
|
||||
# or check logs:
|
||||
tail -f logs/nanoclaw.log
|
||||
sleep 3 && launchctl list | grep nanoclaw
|
||||
container ls -a --format json | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
nc = [c for c in data if c['configuration']['id'].startswith('nanoclaw-')]
|
||||
print(f'{len(nc)} nanoclaw container(s)')
|
||||
for c in nc: print(f' {c[\"configuration\"][\"id\"]} - {c[\"status\"]}')
|
||||
"
|
||||
```
|
||||
|
||||
### Step 8: Test Voice Transcription
|
||||
@@ -389,6 +431,41 @@ The architecture supports multiple providers. To add Groq, Deepgram, or local Wh
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Agent doesn't respond to voice messages (or any messages after a voice note)
|
||||
|
||||
**Most likely cause: orphaned containers.** When the service restarts, the previous container keeps running and races to consume IPC messages. Check:
|
||||
|
||||
```bash
|
||||
container ls -a --format json | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
nc = [c for c in data if c['configuration']['id'].startswith('nanoclaw-')]
|
||||
print(f'{len(nc)} nanoclaw container(s):')
|
||||
for c in nc: print(f' {c[\"configuration\"][\"id\"]} - {c[\"status\"]}')
|
||||
"
|
||||
```
|
||||
|
||||
If you see more than one running container, kill the orphans:
|
||||
|
||||
```bash
|
||||
# Stop all nanoclaw containers, then restart the service
|
||||
container ls -a --format json | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
running = [c['configuration']['id'] for c in data if c['configuration']['id'].startswith('nanoclaw-') and c['status'] == 'running']
|
||||
if running: print(' '.join(running))
|
||||
" | xargs -r container stop 2>/dev/null
|
||||
container ls -a --format json | python3 -c "
|
||||
import sys, json
|
||||
data = json.load(sys.stdin)
|
||||
nc = [c['configuration']['id'] for c in data if c['configuration']['id'].startswith('nanoclaw-')]
|
||||
if nc: print(' '.join(nc))
|
||||
" | xargs -r container rm 2>/dev/null
|
||||
launchctl kickstart -k gui/$(id -u)/com.nanoclaw
|
||||
```
|
||||
|
||||
**Root cause:** The `ensureContainerSystemRunning()` function previously used `container ls --format {{.Names}}` which silently fails on Apple Container (only `json` and `table` formats are supported). Step 6 of this skill fixes this. If you haven't applied Step 6, the orphan problem will recur on every restart.
|
||||
|
||||
### "Transcription unavailable" or "Transcription failed"
|
||||
|
||||
Check logs for specific errors:
|
||||
@@ -417,13 +494,11 @@ const __dirname = dirname(__filename);
|
||||
|
||||
### Dependency conflicts (Zod versions)
|
||||
|
||||
If you see Zod version conflicts during `npm install`:
|
||||
The OpenAI SDK requires Zod v3, but NanoClaw uses Zod v4. This conflict is guaranteed — always use:
|
||||
```bash
|
||||
npm install --legacy-peer-deps
|
||||
```
|
||||
|
||||
This resolves conflicts between OpenAI SDK (requires Zod v3) and other dependencies.
|
||||
|
||||
---
|
||||
|
||||
## Security Notes
|
||||
|
||||
Reference in New Issue
Block a user