TryGhost/Ghost · error · Error

Build image not found: ${BUILD_IMAGE}\n\nYou are running in

Error message

Build image not found: ${BUILD_IMAGE}\n\nYou are running in "build" mode, which requires a pre-built Docker image.\nFor local development, "dev" mode is recommended instead.\n\nTo fix this, either:\n  1. (Recommended) Run "pnpm dev" first, then re-run tests — dev mode is auto-detected and doesn't need this image\n  2. Build locally: pnpm --filter @tryghost/e2e build:docker (with GHOST_E2E_BASE_IMAGE set)\n  3. Pull from registry: docker pull ${BUILD_IMAGE}\n  4. Use a different image: GHOST_E2E_MODE=build GHOST_E2E_IMAGE=<image> pnpm --filter @tryghost/e2e test

What it means

GhostManager.verifyBuildImageExists() probes the local Docker image store with docker.getImage(BUILD_IMAGE).inspect() before running build-mode tests. If inspect rejects (image absent), it throws with remediation steps. A static cache (verifiedBuildImageKey) prevents re-inspecting once verified within the process. This fires only in GHOST_E2E_MODE=build, where tests assume a pre-built production Ghost image rather than a live dev server.

Source

Thrown at e2e/helpers/environment/service-managers/ghost-manager.ts:127

    }

    /**
     * Verify the build image exists locally.
     * Fails early with a helpful error message if the image is not available.
     */
    async verifyBuildImageExists(): Promise<void> {
        const buildImageKey = `${BUILD_IMAGE}\n${BUILD_GATEWAY_IMAGE}`;
        if (GhostManager.verifiedBuildImageKey === buildImageKey) {
            debug(`Build images already verified: ${BUILD_IMAGE}, ${BUILD_GATEWAY_IMAGE}`);
            return;
        }

        try {
            const image = this.docker.getImage(BUILD_IMAGE);
            await image.inspect();
            debug(`Build image verified: ${BUILD_IMAGE}`);
        } catch {
            throw new Error(
                `Build image not found: ${BUILD_IMAGE}\n\n` +
                `You are running in "build" mode, which requires a pre-built Docker image.\n` +
                `For local development, "dev" mode is recommended instead.\n\n` +
                `To fix this, either:\n` +
                `  1. (Recommended) Run "pnpm dev" first, then re-run tests — dev mode is auto-detected and doesn't need this image\n` +
                `  2. Build locally: pnpm --filter @tryghost/e2e build:docker (with GHOST_E2E_BASE_IMAGE set)\n` +
                `  3. Pull from registry: docker pull ${BUILD_IMAGE}\n` +
                `  4. Use a different image: GHOST_E2E_MODE=build GHOST_E2E_IMAGE=<image> pnpm --filter @tryghost/e2e test`
            );
        }

        try {
            const gatewayImage = this.docker.getImage(BUILD_GATEWAY_IMAGE);
            await gatewayImage.inspect();
            debug(`Build gateway image verified: ${BUILD_GATEWAY_IMAGE}`);
        } catch {
            throw new Error(
                `Build gateway image not found: ${BUILD_GATEWAY_IMAGE}\n\n` +

View on GitHub (pinned to 47d8b0e2ad)

Solutions

  1. Run pnpm dev first and re-run tests — dev mode is auto-detected and bypasses this image entirely (recommended for local dev).
  2. Build the image locally: pnpm --filter @tryghost/e2e build:docker with GHOST_E2E_BASE_IMAGE set.
  3. Pull the image: docker pull ${BUILD_IMAGE}.
  4. Point at an existing image: GHOST_E2E_MODE=build GHOST_E2E_IMAGE=<image> pnpm --filter @tryghost/e2e test.

Example fix

# before
GHOST_E2E_MODE=build pnpm --filter @tryghost/e2e test

# after (pick one)
pnpm dev && pnpm --filter @tryghost/e2e test
# or
pnpm --filter @tryghost/e2e build:docker && GHOST_E2E_MODE=build pnpm --filter @tryghost/e2e test
Defensive patterns

Strategy: validation

Validate before calling

import Docker from 'dockerode';
const docker = new Docker();
async function imageExists(tag: string): Promise<boolean> {
    try { await docker.getImage(tag).inspect(); return true; }
    catch { return false; }
}
// call before running build-mode tests:
if (GHOST_E2E_MODE === 'build' && !await imageExists(BUILD_IMAGE)) {
    throw new Error(`Pre-flight: ${BUILD_IMAGE} missing. Run pnpm --filter @tryghost/e2e build:docker.`);
}

Prevention

When it happens

Trigger: GHOST_E2E_MODE=build set without first building the image. BUILD_IMAGE (derived from GHOST_E2E_IMAGE / registry tag) doesn't exist locally and wasn't pulled. The image was built under a different tag than BUILD_IMAGE resolves to. A previous docker system prune removed it.

Common situations: Running the build-mode test suite in a fresh CI worker that never ran build:docker; switching GHOST_E2E_BASE_IMAGE without rebuilding; tagging mismatch between the build pipeline and what the test resolves; running build mode when dev mode (pnpm dev) was intended.

Related errors


AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13). Data as JSON: /api/errors/441ccd2bc25d3cae. Report an issue: GitHub.