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
- Run pnpm dev first and re-run tests — dev mode is auto-detected and bypasses this image entirely (recommended for local dev).
- Build the image locally: pnpm --filter @tryghost/e2e build:docker with GHOST_E2E_BASE_IMAGE set.
- Pull the image: docker pull ${BUILD_IMAGE}.
- 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
- Default to dev mode (pnpm dev) for local work; it bypasses the build image entirely.
- Add a docker image inspect pre-flight to CI before invoking the build-mode suite.
- Pin GHOST_E2E_BASE_IMAGE and build in the same pipeline that runs build-mode tests.
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
- Build gateway image not found: ${BUILD_GATEWAY_IMAGE}\n\nTo
- Cannot create without a persistence adapter. Use buildMany()
- Egress monitor sidecar did not start in time
- Egress monitor container has no network IP
- Ghost container became unhealthy during initialization
AI-assisted analysis of TryGhost/Ghost@47d8b0e2ad (2026-08-13).
Data as JSON: /api/errors/441ccd2bc25d3cae.
Report an issue: GitHub.