Skip to content

ensureDockerImage() silently overwrites custom sandbox image with plain debian #51185

@dpalis

Description

@dpalis

Summary

ensureDockerImage() in dist/reply-Bm8VrLQh.js has a hardcoded fallback that runs docker tag debian:bookworm-slim openclaw-sandbox:bookworm-slim whenever the sandbox image is missing. This silently replaces any custom-built image (with python3, jq, ripgrep, etc.) with plain debian — breaking Write/Edit tools that depend on python3.

This is the root cause behind #45108, #51099, and partially #24151.

Mechanism

async function ensureDockerImage(image) {
    if (await dockerImageExists(image)) return;
    if (image === "openclaw-sandbox:bookworm-slim") {
        await execDocker(["pull", "debian:bookworm-slim"]);
        await execDocker(["tag", "debian:bookworm-slim", DEFAULT_SANDBOX_IMAGE]);
        return;
    }
    throw new Error(`Sandbox image not found`);
}

The function checks if the image exists. If not, it pulls debian:bookworm-slim and tags it directly — without checking for a local Dockerfile (e.g., scripts/Dockerfile.sandbox).

Reproduction

  1. Build a custom sandbox image: docker build -t openclaw-sandbox:bookworm-slim -f scripts/Dockerfile.sandbox scripts/
  2. Verify: docker run --rm openclaw-sandbox:bookworm-slim python3 --versionPython 3.11.2
  3. Remove the image: docker rmi openclaw-sandbox:bookworm-slim
  4. Send any message via Telegram (triggers sandbox creation)
  5. Verify: docker inspect openclaw-sandbox:bookworm-slim --format '{{.Id}}' matches debian:bookworm-slim — same image ID, no python3

This happens after any docker system prune, docker image prune, or manual image removal.

Expected behavior

ensureDockerImage() should check for a user-provided Dockerfile before falling back to tagging plain debian:

async function ensureDockerImage(image) {
    if (await dockerImageExists(image)) return;
    if (image === "openclaw-sandbox:bookworm-slim") {
        const dockerfilePath = path.join(projectRoot, "scripts/Dockerfile.sandbox");
        if (await fileExists(dockerfilePath)) {
            await execDocker(["build", "-t", DEFAULT_SANDBOX_IMAGE, "-f", dockerfilePath, path.dirname(dockerfilePath)]);
            return;
        }
        await execDocker(["pull", "debian:bookworm-slim"]);
        await execDocker(["tag", "debian:bookworm-slim", DEFAULT_SANDBOX_IMAGE]);
        return;
    }
    throw new Error(`Sandbox image not found`);
}

Or alternatively, support a sandbox.docker.buildFile config key that points to the Dockerfile.

Impact

  • Write/Edit tools break silently (python3 not found)
  • Agents fall back to exec with heredoc (unreliable workaround)
  • Users must manually rebuild the image after every prune — with no warning that it was overwritten

Related issues

Environment

  • openclaw v2026.3.13
  • macOS (Darwin 25.3.0, arm64)
  • Docker Desktop

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions