affaan-m/ECC · error · Error

Failed to save package manager preference: ${err.message}

Error message

Failed to save package manager preference: ${err.message}

What it means

Thrown by setPreferredPackageManager when saveConfig(config) throws while writing the global preference file (~/.claude/package-manager.json via getConfigPath). The underlying write error (permission, ENOSPC, ENOENT for a missing ~/.claude, EISDIR) is wrapped with context so the caller knows the preference was validated but could not be persisted. The in-memory config is not rolled back because saveConfig is the failing step.

Source

Thrown at scripts/lib/package-manager.js:256

  };
}

/**
 * Set user's preferred package manager (global)
 */
function setPreferredPackageManager(pmName) {
  if (!PACKAGE_MANAGERS[pmName]) {
    throw new Error(`Unknown package manager: ${pmName}`);
  }

  const config = loadConfig() || {};
  config.packageManager = pmName;
  config.setAt = new Date().toISOString();

  try {
    saveConfig(config);
  } catch (err) {
    throw new Error(`Failed to save package manager preference: ${err.message}`);
  }

  return config;
}

/**
 * Set project's preferred package manager
 */
function setProjectPackageManager(pmName, projectDir = process.cwd()) {
  if (!PACKAGE_MANAGERS[pmName]) {
    throw new Error(`Unknown package manager: ${pmName}`);
  }

  const configDir = path.join(projectDir, '.claude');
  const configPath = path.join(configDir, 'package-manager.json');

  const config = {
    packageManager: pmName,

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Create ~/.claude and ensure it is writable: mkdir -p ~/.claude && chown -R $USER ~/.claude.
  2. Verify HOME is set and writable: echo $HOME and touch "$HOME/.claude/test".
  3. Free disk space if ENOSPC; remove the path if it is a directory/file conflict (EISDIR/EEXIST).
  4. Run the tool as the user that owns HOME rather than a restricted service account.

Example fix

# before: ~/.claude missing or owned by root
setPreferredPackageManager('pnpm'); // throws [297]

# after: ensure writable config dir
mkdir -p ~/.claude
sudo chown -R "$USER:$USER" ~/.claude
# then re-run
setPreferredPackageManager('pnpm');
Defensive patterns

Strategy: try-catch

Validate before calling

const fs = require('fs'); const path = require('path');
function assertGlobalConfigWritable() {
  const { getClaudeDir } = require('./scripts/lib/utils');
  const dir = getClaudeDir();
  fs.mkdirSync(dir, { recursive: true });
  const probe = path.join(dir, '.ecc-write-probe');
  fs.writeFileSync(probe, ''); fs.unlinkSync(probe);
}
assertGlobalConfigWritable();
setPreferredPackageManager('pnpm');

Type guard

null

Try / catch

try {
  setPreferredPackageManager(pmName);
} catch (err) {
  if (/Failed to save package manager preference/.test(err.message)) {
    // ensure ~/.claude exists and is writable, then retry once
    fs.mkdirSync(path.join(os.homedir(), '.claude'), { recursive: true });
    setPreferredPackageManager(pmName);
  } else throw err;
}

Prevention

When it happens

Trigger: Fires in the catch of saveConfig inside setPreferredPackageManager. Occurs when writeFile to getConfigPath() fails: ~/.claude does not exist, the path is not writable by the current user, the disk is full, the path resolves to a directory, or a read-only HOME (container/sandbox).

Common situations: First run on a system where ~/.claude was never created and utils.writeFile does not mkdir; HOME points to a read-only or non-existent dir; running as a user without write access to HOME; disk full; ~/.claude accidentally turned into a regular file or owned by root.

Related errors


AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13). Data as JSON: /api/errors/da0409b431590f21. Report an issue: GitHub.