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
- Build or pull the image first: `docker pull <image>` or `docker build -t <image> .`.
- Verify the exact reference exists: `docker image ls | grep <image>` (tags must match exactly, including :latest).
- If you don't need a custom image, omit the `image` option so the plugin's default image is auto-built.
- 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
- Pull or build every custom image before spawnDockerComputer.
- Prefer omitting `image` so the plugin's default image is auto-built.
- Watch for CI image prunes that remove prebuilt images between runs.
- Copy image references exactly, including the tag.
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
- cleanup_failed
- docker exec : agent returned no JSON receipt
- docker exec exited
- docker exec : timed out
- docker_unavailable
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)