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

  1. 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).
  2. If the lockfile drifted, regenerate it locally with `bun install` (no --frozen-lockfile) and commit the updated bun.lockb before re-running.
  3. 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`).
  4. 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).
  5. 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

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


AI-assisted analysis of thedotmack/claude-mem@d768ba3643 (2026-08-12). Data as JSON: /api/errors/caed17f3c50f1856. Report an issue: GitHub.