can1357/oh-my-pi · error

Runtime install at ${runtimeDir} declares no dependencies

Error message

Runtime install at ${runtimeDir} declares no dependencies

What it means

Before probing node_modules to see if a runtime is already installed, ensureRuntimeInstalled picks the first dependency name from the install manifest as the probe package. If the manifest declares no dependencies at all, there is nothing to probe for, so this error is thrown — an empty dependency set is treated as a configuration bug rather than a no-op.

Source

Thrown at packages/utils/src/runtime-install.ts:423

 * cross-process safe). Returns `runtimeDir`.
 *
 * Serialization uses the OS-backed {@link withFileLock} at
 * `${runtimeDir}.install.lock`, which the kernel releases on process death, so
 * a crashed installer cannot wedge later attempts (issue #10120). The path is
 * deliberately distinct from the legacy `${runtimeDir}.lock` mkdir directory;
 * {@link withLegacyInstallLock} atomically reserves that namespace during the
 * new install so older processes cannot cross the migration boundary.
 */
export async function ensureRuntimeInstalled(options: EnsureRuntimeInstalledOptions): Promise<string> {
	const { runtimeDir, install, onPhase, lockAttempts = 240, lockSleepMs = 250 } = options;
	let probePackage = options.probePackage;
	if (!probePackage) {
		for (const name in install.dependencies) {
			probePackage = name;
			break;
		}
	}
	if (!probePackage) throw new Error(`Runtime install at ${runtimeDir} declares no dependencies`);
	const probeManifest = Bun.file(path.join(runtimeDir, "node_modules", ...probePackage.split("/"), "package.json"));
	if (await probeManifest.exists()) return runtimeDir;

	onPhase?.("initiate");
	// withFileLock does not create parent directories; the runtime cache dir may
	// not exist yet on the very first install.
	await fsp.mkdir(path.dirname(runtimeDir), { recursive: true });
	return withFileLock(
		`${runtimeDir}.install`,
		() =>
			withLegacyInstallLock(runtimeDir, lockSleepMs, async () => {
				if (await probeManifest.exists()) return runtimeDir;
				await writeRuntimeManifest(runtimeDir, install);
				onPhase?.("download");
				await runRuntimeInstall(runtimeDir);
				onPhase?.("done");
				return runtimeDir;
			}),

View on GitHub (pinned to 9690622007)

Solutions

  1. Add at least one entry to the manifest's dependencies object.
  2. Regenerate the manifest if it is produced by a tool that failed to collect dependencies.
  3. Validate the manifest (dependencies non-empty) before calling install.
  4. Skip the install step entirely for zero-dependency runtimes instead of calling install.

Example fix

// before
const install = { dependencies: {} };
await ensureRuntimeInstalled(dir, install);
// after
if (Object.keys(install.dependencies).length > 0) {
  await ensureRuntimeInstalled(dir, install);
}
Defensive patterns

Strategy: validation

Validate before calling

if (!manifest.dependencies || Object.keys(manifest.dependencies).length === 0) {
  throw new Error('runtime manifest must declare at least one dependency');
}

Type guard

function hasDependencies(m: { dependencies: Record<string, string> }): boolean {
  return Object.keys(m.dependencies ?? {}).length > 0;
}

Try / catch

if (!hasDependencies(manifest)) {
  logger.warn('skipping install: empty dependency set');
} else {
  await ensureRuntimeInstalled(dir, manifest);
}

Prevention

When it happens

Trigger: Calling ensureRuntimeInstalled (or the install/runtimeDir helpers) with a manifest whose `dependencies` object is empty ({}), or a malformed manifest where no enumerable dependency keys exist.

Common situations: Hand-written runtime manifests with an empty dependencies block; a generator emitting `{}` when dependency extraction failed; JSON5/JSON edits that accidentally emptied the object.

Related errors


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