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

  1. Read the stderrTail in the log entry — it names the failing component
  2. Rebuild the image cleanly: prune builder cache then `./container/build.sh`
  3. Check memory limits in container config and host RAM
  4. 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

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


AI-assisted analysis of nanocoai/nanoclaw@294ef2aee8 (2026-08-28). Data as JSON: /api/errors/48e11701c385ddd9. Report an issue: GitHub.