nanocoai/nanoclaw · warning
Container exited non-zero
Error message
Container exited non-zero
What it means
A session container process exited with a non-zero code while not being intentionally stopped, and stderr lines were captured. This points at a crash inside the container (agent-runner/provider failure) rather than a host-initiated stop.
Source
Thrown at src/drivers/docker-driver.ts:476
) {}
async start(): Promise<void> {
if (this.#proc) return; // idempotent
// `start --attach` is the supervision channel: it exits with the container's
// exit code and streams the stderr that explains a boot failure. A container
// that dies at boot (unknown provider, missing binary, bad config) explains
// itself only here, so keep a tail and surface it at warn on a non-zero exit.
const proc = this.cli.start(['start', '--attach', this.name]);
this.#proc = proc;
proc.onStderr((line) => {
log.debug(line, { container: this.name });
this.#stderrTail.push(line);
if (this.#stderrTail.length > 10) this.#stderrTail.shift();
});
proc.onExit((code) => {
this.#attachExitCode = code;
if (!this.#stopping && code !== 0 && code !== null && this.#stderrTail.length > 0) {
log.warn('Container exited non-zero', { containerName: this.name, code, stderrTail: this.#stderrTail });
}
// Every observed terminal transition rides the driver-level stream —
// even ends the host requested. Intent filtering is the hub's job.
this.emit({ key: this.key, kind: 'terminal' });
});
}
async status(): Promise<SessionStatus> {
let state: string;
try {
state = this.cli.run(['inspect', '--format', '{{.State.Status}}|{{.State.ExitCode}}', this.name]).trim();
} catch {
// `--rm` means an exited container has already been removed; the attach
// exit code is the only record of how it ended. A host-requested stop is
// not a failure, whatever code the runtime used to end the process.
if (!this.#stopping && typeof this.#attachExitCode === 'number' && this.#attachExitCode !== 0) {
return {
phase: 'failed',View on GitHub (pinned to 294ef2aee8)
Solutions
- Read the stderrTail in the log entry — it names the failing component
- Rebuild the image cleanly: prune builder cache then `./container/build.sh`
- Check memory limits in container config and host RAM
- Retry by sending a message (host respawns the session container)
Defensive patterns
Strategy: try-catch
Validate before calling
import { execSync } from 'node:child_process';
execSync(`docker inspect nanoclaw-agent:latest`); Try / catch
proc.onExit(code => { if (code !== 0 && !stopping) inspectStderrTail(); }); Prevention
- Build images with a pruned builder cache to avoid corruption
- Set adequate container memory limits
- Capture stderr tails immediately — container logs are lost after exit (--rm)
When it happens
Trigger: Container entrypoint (`bun ...` agent-runner) exits non-zero — provider SDK crash, missing file in the image, OOM kill, or a runtime panic. Logged only when a stderr tail was captured.
Common situations: OOM (container memory limits), corrupted image after a partial rebuild, or a provider CLI (claude-code) crashing on startup.
Related errors
- --stdin-json input exceeds ${MAX_STDIN_JSON_BYTES} bytes
- --stdin-json input is not valid JSON
- --stdin-json input must be one JSON object
- wakeContainer failed — host-sweep will retry
- Failed to list existing sessions for adoption
AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28).
Data as JSON: /api/errors/48e11701c385ddd9.
Report an issue: GitHub.