{"record":{"id":"d3853080e7e2d134","repo":"can1357/oh-my-pi","slug":"runtime-install-at-runtimedir-declares-no-depen","errorCode":null,"errorMessage":"Runtime install at ${runtimeDir} declares no dependencies","messagePattern":"Runtime install at (.+?) declares no dependencies","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/utils/src/runtime-install.ts","lineNumber":423,"sourceCode":" * cross-process safe). Returns `runtimeDir`.\n *\n * Serialization uses the OS-backed {@link withFileLock} at\n * `${runtimeDir}.install.lock`, which the kernel releases on process death, so\n * a crashed installer cannot wedge later attempts (issue #10120). The path is\n * deliberately distinct from the legacy `${runtimeDir}.lock` mkdir directory;\n * {@link withLegacyInstallLock} atomically reserves that namespace during the\n * new install so older processes cannot cross the migration boundary.\n */\nexport async function ensureRuntimeInstalled(options: EnsureRuntimeInstalledOptions): Promise<string> {\n\tconst { runtimeDir, install, onPhase, lockAttempts = 240, lockSleepMs = 250 } = options;\n\tlet probePackage = options.probePackage;\n\tif (!probePackage) {\n\t\tfor (const name in install.dependencies) {\n\t\t\tprobePackage = name;\n\t\t\tbreak;\n\t\t}\n\t}\n\tif (!probePackage) throw new Error(`Runtime install at ${runtimeDir} declares no dependencies`);\n\tconst probeManifest = Bun.file(path.join(runtimeDir, \"node_modules\", ...probePackage.split(\"/\"), \"package.json\"));\n\tif (await probeManifest.exists()) return runtimeDir;\n\n\tonPhase?.(\"initiate\");\n\t// withFileLock does not create parent directories; the runtime cache dir may\n\t// not exist yet on the very first install.\n\tawait fsp.mkdir(path.dirname(runtimeDir), { recursive: true });\n\treturn withFileLock(\n\t\t`${runtimeDir}.install`,\n\t\t() =>\n\t\t\twithLegacyInstallLock(runtimeDir, lockSleepMs, async () => {\n\t\t\t\tif (await probeManifest.exists()) return runtimeDir;\n\t\t\t\tawait writeRuntimeManifest(runtimeDir, install);\n\t\t\t\tonPhase?.(\"download\");\n\t\t\t\tawait runRuntimeInstall(runtimeDir);\n\t\t\t\tonPhase?.(\"done\");\n\t\t\t\treturn runtimeDir;\n\t\t\t}),","sourceCodeStart":405,"sourceCodeEnd":441,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/utils/src/runtime-install.ts#L405-L441","documentation":"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.","triggerScenarios":"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.","commonSituations":"Hand-written runtime manifests with an empty dependencies block; a generator emitting `{}` when dependency extraction failed; JSON5/JSON edits that accidentally emptied the object.","solutions":["Add at least one entry to the manifest's dependencies object.","Regenerate the manifest if it is produced by a tool that failed to collect dependencies.","Validate the manifest (dependencies non-empty) before calling install.","Skip the install step entirely for zero-dependency runtimes instead of calling install."],"exampleFix":"// before\nconst install = { dependencies: {} };\nawait ensureRuntimeInstalled(dir, install);\n// after\nif (Object.keys(install.dependencies).length > 0) {\n  await ensureRuntimeInstalled(dir, install);\n}","handlingStrategy":"validation","validationCode":"if (!manifest.dependencies || Object.keys(manifest.dependencies).length === 0) {\n  throw new Error('runtime manifest must declare at least one dependency');\n}","typeGuard":"function hasDependencies(m: { dependencies: Record<string, string> }): boolean {\n  return Object.keys(m.dependencies ?? {}).length > 0;\n}","tryCatchPattern":"if (!hasDependencies(manifest)) {\n  logger.warn('skipping install: empty dependency set');\n} else {\n  await ensureRuntimeInstalled(dir, manifest);\n}","preventionTips":["Validate runtime manifests at build/generation time, not install time.","Fail manifest generators loudly when they produce zero dependencies.","Skip install entirely for intentionally dependency-free runtimes."],"tags":["configuration","manifest","validation"],"backgroundTag":"empty-dependency-manifest","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}