{"record":{"id":"e6ddd7bc7fbdcd5f","repo":"TryGhost/Ghost","slug":"timeout-waiting-for-ghost-to-become-ready","errorCode":null,"errorMessage":"Timeout waiting for Ghost to become ready","messagePattern":"Timeout waiting for Ghost to become ready","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"e2e/helpers/environment/service-managers/ghost-manager.ts","lineNumber":522,"sourceCode":"                throw new Error('Ghost container became unhealthy during initialization');\n            }\n\n            if (!info.State.Running) {\n                const logs = await container.logs({stdout: true, stderr: true, tail: 100});\n                logging.error(`Container stopped unexpectedly:\\n${logs.toString()}`);\n                throw new Error('Ghost container stopped during initialization');\n            }\n\n            // Still starting - wait and check again\n            await new Promise((r) => {\n                setTimeout(r, READINESS_POLL_INTERVAL_MS);\n            });\n        }\n\n        // Timeout\n        const logs = await container.logs({stdout: true, stderr: true, tail: 100});\n        logging.error(`Timeout waiting for container. Last logs:\\n${logs.toString()}`);\n        throw new Error('Timeout waiting for Ghost to become ready');\n    }\n\n    private async probeHostReadiness(): Promise<boolean> {\n        const controller = new AbortController();\n        const timeout = setTimeout(() => controller.abort(), 500);\n\n        try {\n            const response = await fetch(`http://localhost:${this.getGatewayPort()}/ghost/api/admin/authentication/setup`, {\n                method: 'GET',\n                headers: {Accept: 'application/json'},\n                signal: controller.signal\n            });\n            const body = await response.json().catch(() => null) as {setup?: Array<{status?: unknown}>} | null;\n\n            return response.ok && Array.isArray(body?.setup) && typeof body.setup[0]?.status === 'boolean';\n        } catch (error) {\n            debug('Host readiness probe failed:', error);\n            return false;","sourceCodeStart":504,"sourceCodeEnd":540,"githubUrl":"https://github.com/TryGhost/Ghost/blob/47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe/e2e/helpers/environment/service-managers/ghost-manager.ts#L504-L540","documentation":"The readiness loop's overall timeout elapsed: the container is still running and not unhealthy, but probeHostReadiness() (fetch to /ghost/api/admin/authentication/setup through the gateway) never returned ok within the budget (default 120000ms, polled every READINESS_POLL_INTERVAL_MS). The manager dumps logs and throws. The container is slow to boot, not broken.","triggerScenarios":"Ghost is genuinely slow to start (cold cache, slow migrations, slow DB). The gateway path is misrouted so the probe never reaches Ghost. Network/proxy overhead on the gateway port. Heavy CI machine under load.","commonSituations":"First-run DB migrations on a fresh schema; overloaded CI runner; gateway port forwarding broken; Ghost boot loop that hasn't yet tripped the healthcheck; tight default timeout on a slow arch.","solutions":["Raise the timeout: call waitForReady(<larger ms>) or increase the default if the suite consistently needs more.","Check the dumped logs — if migrations are running, pre-warm them or use a migrated seed image.","Verify the gateway routes localhost:<getGatewayPort()> to the Ghost container; test the URL manually.","Reduce concurrent worker count on the CI runner to lower boot contention."],"exampleFix":"// before\nawait ghostManager.waitForReady(); // default 120000ms\n\n// after\nawait ghostManager.waitForReady(240000); // allow slow boots / migrations","handlingStrategy":"retry","validationCode":"// Confirm the gateway path is reachable before the test runs:\nconst ok = await fetch(`http://localhost:${gatewayPort}/ghost/api/admin/authentication/setup`).then(r => r.ok).catch(() => false);","typeGuard":null,"tryCatchPattern":"try {\n    await ghostManager.waitForReady(120000);\n} catch (e) {\n    // Retry once with a larger budget for slow boots / first-run migrations.\n    await ghostManager.waitForReady(240000);\n}","preventionTips":["Scale the timeout to the slowest expected boot (cold migrations, loaded CI).","Pre-warm migrations with a seeded DB image to shorten boot time.","Reduce concurrent workers to lower boot contention on shared runners."],"tags":["e2e","timeout","readiness","ci-performance","gateway"],"backgroundTag":null,"analyzedSha":"47d8b0e2ad2fd4757d3bc45f46c3ac165ff8a1fe","analyzedAt":"2026-08-13T01:25:26.651Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}