Hmbown/CodeWhale · error · SpawnError

spawn_image_missing

spawn_image_missing

Error message

docker image "${image}" is not present locally

What it means

Thrown by ensureImage() when a custom image (anything other than the plugin's DEFAULT_IMAGE) is requested but `docker image inspect` shows it is not present locally. The plugin deliberately never builds or pulls user-supplied images — the caller must provide them locally first.

Solutions

  1. Build or pull the image first: `docker pull <image>` or `docker build -t <image> .`.
  2. Verify the exact reference exists: `docker image ls | grep <image>` (tags must match exactly, including :latest).
  3. If you don't need a custom image, omit the `image` option so the plugin's default image is auto-built.
  4. In CI, add a step that pulls/prepares the image before spawning the computer.

Example fix

// before
await spawnDockerComputer({ image: "team/desktop:v9" }); // never pulled
// after
await dockerOk(["pull", "team/desktop:v9"]);
await spawnDockerComputer({ image: "team/desktop:v9" });
Defensive patterns

Strategy: validation

Validate before calling

import { execFile } from "node:child_process";
import { promisify } from "node:util";
const pexec = promisify(execFile);
async function imageExists(image) {
  try { await pexec("docker", ["image", "inspect", image]); return true; }
  catch { return false; }
}

Try / catch

try {
  await spawnDockerComputer({ image: custom });
} catch (e) {
  if (e?.code === "spawn_image_missing") {
    // fall back to the default image or pull the custom one, then retry
  }
}

Prevention

When it happens

Trigger: Calling spawnDockerComputer({ image: "my-desktop:v2" }) where the image has never been built or pulled on this machine, so `docker image inspect my-desktop:v2` exits non-zero and image !== DEFAULT_IMAGE.

Common situations: Fresh machine or CI runner without the prebuilt image, image tag renamed upstream, prune/cleanup jobs removed the image, or a typo in the image reference.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/866626aabe88a662. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/plugins/computer-use/src/spawn.mjs:62

  if (r.code !== 0) throw new SpawnError("spawn_failed", `docker ${args[0]} failed: ${trim(r.stderr || r.stdout)}`, r);
  return r;
}

export async function dockerAvailable(command = docker) {
  const r = await command(["info", "--format", "{{.OSType}}"], { timeoutMs: 10_000 });
  return r.code === 0 && !r.timedOut && !r.aborted && r.stdout.trim() === "linux";
}

/**
 * The image must exist locally. The plugin's own image is built from
 * docker/Dockerfile on first use; any other image name is the caller's
 * responsibility — we never guess a build context for it.
 */
async function ensureImage(image) {
  const inspect = await docker(["image", "inspect", image], { timeoutMs: 15_000 });
  if (inspect.code === 0) return { built: false };
  if (image !== DEFAULT_IMAGE) {
    throw new SpawnError("spawn_image_missing", `docker image "${image}" is not present locally`);
  }
  const r = await docker(["build", "-t", image, "-f", path.join(PLUGIN_ROOT, "docker", "Dockerfile"), PLUGIN_ROOT], { timeoutMs: 15 * 60_000 });
  if (r.aborted) throw Object.assign(new ExecError("computer request cancelled", r), { code: "cancelled" });
  if (r.code !== 0) throw new SpawnError("spawn_failed", `docker build ${image} failed: ${trim(r.stderr || r.stdout, 800)}`, r);
  return { built: true };
}

/**
 * Start a disposable desktop container and verify the agent answers inside
 * its session. On any failure the container is removed — spawn is
 * transactional: either a live computer comes back or nothing was left.
 */
export async function spawnDockerComputer({ id, image = DEFAULT_IMAGE } = {}) {
  if (!await dockerAvailable()) {
    throw new SpawnError("docker_unavailable", "A Linux Docker engine is required — start Docker Desktop in Linux-container mode (or Colima) and spawn again");
  }
  const { built } = await ensureImage(image);
  const container = `cu-spawn-${id}-${crypto.randomBytes(3).toString("hex")}`;

View on GitHub (pinned to 73e0f67d83)