thedotmack/claude-mem · error
bun install failed in ${targetDir} ${describeExecError(err)}
Error message
bun install failed in ${targetDir}
${describeExecError(err)} What it means
Thrown by installPluginDependencies() during the npx-cli bootstrap when the underlying `bun install --frozen-lockfile --ignore-scripts` child process exits non-zero, times out, or crashes. The error message is built with describeExecError(), which attaches the exec stdout/stderr that Bun produced (explicitly Object.assigned onto the rejection at setup-runtime.ts:448) so the root cause is visible. This is the last-resort surface for any failure in plugin dependency installation.
Source
Thrown at src/npx-cli/install/setup-runtime.ts:455
// Async exec (not execSync): a blocked event loop freezes the installer's
// clack spinner for the duration of the install, which reads as a stall.
const runBunInstall = (): Promise<void> =>
new Promise<void>((resolve, reject) => {
exec(`${bunCmd} install --frozen-lockfile --ignore-scripts`, {
cwd: targetDir,
timeout: INSTALL_TIMEOUT_MS,
maxBuffer: 16 * 1024 * 1024,
...(IS_WINDOWS ? { shell: process.env.ComSpec ?? 'cmd.exe' } : {}),
}, (error, stdout, stderr) =>
// exec errors don't carry stdio; attach so describeExecError can report it.
error ? reject(Object.assign(error, { stdout, stderr })) : resolve());
});
try {
await runBunInstall();
} catch (error) {
const err = error instanceof Error ? error : new Error(String(error));
throw new Error(`bun install failed in ${targetDir}\n${describeExecError(err)}`);
}
verifyCriticalModules(targetDir);
}
export function readInstallMarker(targetDir: string): MarkerSchema | null {
const path = markerPath(targetDir);
if (!existsSync(path)) return null;
const content = readFileSync(path, 'utf-8');
try {
const marker = JSON.parse(content);
if (marker && typeof marker === 'object' && typeof marker.version === 'string') {
return marker as MarkerSchema;
}
} catch {
// Legacy installs wrote only the version string as plain text.
}
View on GitHub (pinned to d768ba3643)
Solutions
- Read the describeExecError output embedded in the message — the attached stdout/stderr names the exact dependency or Bun error code (e.g. ELIFECYCLE, lockfile mismatch).
- If the lockfile drifted, regenerate it locally with `bun install` (no --frozen-lockfile) and commit the updated bun.lockb before re-running.
- If network-related, verify registry reachability (`bun install` in the targetDir manually), check HTTPS_PROXY/registry config, and clear Bun's cache (`bun pm cache rm`).
- If it timed out, retry on a faster network; if a known-slow postinstall is the cause, confirm --ignore-scripts is in effect (it is by default here).
- On Windows with a spaced bunPath, ensure the cmd.exe shell branch at line 445 is taken (ComSpec is set) or move Bun to a space-free path.
Example fix
// before: lockfile drift causes frozen install to fail // regenerate the lockfile locally, then commit $ cd plugin && bun install $ git add bun.lockb && git commit -m "chore: refresh lockfile" // before (Windows, spaced path fails): bunPath = "C:\Program Files\bun.exe" // after: install Bun to a space-free location or rely on the shell fallback $ set ComSpec=C:\Windows\System32\cmd.exe
Defensive patterns
Strategy: try-catch
Validate before calling
import { existsSync } from 'node:fs';
import { join } from 'node:path';
// Run before calling installPluginDependencies
function preflightInstall(targetDir: string, bunPath: string): void {
if (!existsSync(join(targetDir, 'package.json'))) {
throw new Error(`no package.json at ${targetDir}`);
}
if (!existsSync(bunPath)) {
throw new Error(`bun not found at ${bunPath}`);
}
if (!existsSync(join(targetDir, 'bun.lockb')) && !existsSync(join(targetDir, 'bun.lock'))) {
console.warn('warning: no lockfile; --frozen-lockfile will fail unless one exists');
}
} Try / catch
try {
await installPluginDependencies(targetDir, bunPath);
} catch (error) {
// The thrown Error embeds describeExecError output (stdout/stderr attached).
const msg = error instanceof Error ? error.message : String(error);
if (/lockfile|frozen/i.test(msg)) {
// drift: regenerate, then retry once
} else if (/ETIMEDOUT|timeout/i.test(msg)) {
// network/timeout: retry with backoff
} else {
throw error; // surface unknown causes
}
} Prevention
- Always regenerate bun.lockb after editing package.json so --frozen-lockfile succeeds.
- Pin a Bun version and verify it on PATH in CI before invoking the installer.
- Run installs on a network with registry access; configure HTTPS_PROXY/registry mirrors for air-gapped CI.
- On Windows, install Bun in a space-free path or ensure ComSpec points at cmd.exe.
When it happens
Trigger: Running `bun install --frozen-lockfile` against a targetDir whose bun.lockb is out of sync with package.json (frozen-lockfile rejects drift); no network/offline registry access; INSTALL_TIMEOUT_MS exceeded by a slow registry or a hung tree-sitter-swift nested postinstall; on Windows, a bunPath containing spaces when shell quoting fails; maxBuffer (16MB) exceeded by noisy install output.
Common situations: A developer edits package.json and forgets to regenerate bun.lockb, so --frozen-lockfile aborts. CI runs in an air-gapped environment with no registry mirror. A corporate proxy intercepts the registry and returns HTML. Bun is the wrong version (older than the lockfile format). A path with spaces on Windows without the cmd.exe shell fallback.
Related errors
- Failed to install Bun. Please install manually: ${manualInst
- Bun installation completed but binary not found. Please rest
- Failed to install uv. Please install manually: ${manualInstr
- SSE stream returned HTTP ${response.status}
- Hand-edited Windows shell string detected in ${filePath} (${
AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12).
Data as JSON: /api/errors/caed17f3c50f1856.
Report an issue: GitHub.