can1357/oh-my-pi · error

Timed out waiting ${timeoutSec} seconds for Vibemon VM ${san

Error message

Timed out waiting ${timeoutSec} seconds for Vibemon VM ${sandbox.id} to become ready

What it means

waitUntilRunning polls the sandbox state every 250ms until timeoutSec elapses. If the sandbox never reports running (and never enters a terminal state) within that window, this timeout error is thrown naming the sandbox id and the configured timeout.

Source

Thrown at packages/metaharness/src/tb/vmon.ts:70

	if (!client) {
		client = connect(config.url, { token: config.token || undefined });
		clients.set(key, client);
	}
	return client;
}

async function waitUntilRunning(sandbox: Sandbox, timeoutSec: number): Promise<void> {
	const deadline = Date.now() + timeoutSec * 1_000;
	while (Date.now() < deadline) {
		const info = await sandbox.refresh();
		const state = String(info.observed_state ?? info.status ?? "").toLowerCase();
		if (state === "running") return;
		if (["exited", "failed", "stopped", "terminated"].includes(state)) {
			throw new Error(`Vibemon VM ${sandbox.id} entered ${state} before becoming ready`);
		}
		await Bun.sleep(250);
	}
	throw new Error(`Timed out waiting ${timeoutSec} seconds for Vibemon VM ${sandbox.id} to become ready`);
}

async function archiveDirectory(root: string): Promise<Blob> {
	const entries: Record<string, Uint8Array> = {};
	const visit = async (dir: string): Promise<void> => {
		for (const entry of await fs.readdir(dir, { withFileTypes: true })) {
			const absolute = path.join(dir, entry.name);
			if (entry.isDirectory()) {
				await visit(absolute);
			} else if (entry.isFile()) {
				const relative = path.relative(root, absolute).split(path.sep).join("/");
				entries[relative] = new Uint8Array(await Bun.file(absolute).arrayBuffer());
			}
		}
	};
	await visit(root);
	return new Bun.Archive(entries, { compress: "gzip", level: 6 }).blob();
}

View on GitHub (pinned to 9690622007)

Solutions

  1. Increase the timeoutSec passed to start() to accommodate slow image pulls or hosts
  2. Check the sandbox status endpoint/API for the sandbox id to see the stuck state
  3. Pre-pull or pre-warm the VM image to remove cold-start latency
  4. Retry the start; if the state is permanently stuck, terminate and recreate the sandbox

Example fix

// before
await vm.start({ timeoutSec: 30 });
// after
await vm.start({ timeoutSec: 300 });
Defensive patterns

Strategy: retry

Try / catch

try {
  await vm.start();
} catch (err) {
  if (/Timed out waiting \d+ seconds for Vibemon VM/.test(err.message)) {
    await terminateSandbox(vm.id); // clean up the stuck sandbox
    const fresh = await createSandbox(config); // retry with a fresh instance
    await fresh.start({ timeoutSec: 300 });
    return fresh;
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling start() when the VM boots too slowly (image pull, slow host), or its state endpoint keeps reporting a non-running, non-terminal state (e.g. 'provisioning', 'pending') for longer than timeoutSec.

Common situations: Large VM image downloads on cold caches; overloaded host delaying startup; upstream API stuck reporting 'creating'; timeoutSec set too low for the environment.

Understand the failure class

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/57625236d3585853. Report an issue: GitHub.