different-ai/openwork · error

Timed out waiting for server health

Error message

Timed out waiting for server health

What it means

waitForHealthy polls the opencode server health endpoint until it responds or a deadline expires. Each poll failure's message is retained in lastError; when the loop exhausts, it throws the last seen error, or "Timed out waiting for server health" if none was captured. Indicates the server never became reachable in time.

Source

Thrown at apps/app/src/app/lib/opencode.ts:353

  const pollMs = options?.pollMs ?? 250;

  const start = Date.now();
  let lastError: string | null = null;

  while (Date.now() - start < timeoutMs) {
    try {
      const health = unwrap(await client.global.health());
      if (health.healthy) {
        return health;
      }
      lastError = "Server reported unhealthy";
    } catch (error) {
      lastError = error instanceof Error ? error.message : "Unknown error";
    }
    await new Promise((resolve) => setTimeout(resolve, pollMs));
  }

  throw new Error(lastError ?? "Timed out waiting for server health");
}

View on GitHub (pinned to 2b7df46e8a)

Solutions

  1. Check server stderr/stdout logs captured during spawn for a startup crash.
  2. Verify the configured port/host matches where the server actually listens.
  3. Increase the health-wait deadline or poll interval for slower machines.
  4. Confirm the server binary runs standalone (`opencode serve`) to isolate spawn problems.

Example fix

// before: fixed budget
await waitForHealthy(server, { timeoutMs: 5000 });
// after: larger budget on cold start
await waitForHealthy(server, { timeoutMs: process.env.CI ? 30000 : 10000 });
Defensive patterns

Strategy: retry

Validate before calling

// probe before relying on waitForHealthy's deadline
const reachable = await fetch(`http://${host}:${port}/health`).then(r => r.ok).catch(() => false);
if (!reachable) console.warn("Server port not reachable; startup may have failed.");

Try / catch

try {
  await waitForHealthy(server, { timeoutMs: 15000 });
} catch (err) {
  if (err instanceof Error && /health|Unknown error/i.test(err.message)) {
    // inspect spawned process logs and exit code before giving up
    const logs = server.getStderr?.() ?? "";
    throw new Error(`Server failed to become healthy: ${err.message}\nstderr: ${logs.slice(-500)}`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Spawning/starting the local opencode server and polling health until the deadline passes: server binary failed to start, wrong port, crash on boot, or startup slower than the wait budget.

Common situations: Missing/incompatible opencode server binary, port already in use or firewalled, machine under load slowing startup, first install downloading dependencies while health polls expire.

Understand the failure class

Related errors


AI-assisted analysis of different-ai/openwork@2b7df46e8a (2026-09-01). Data as JSON: /api/errors/8edd4411b9d1e229. Report an issue: GitHub.