affaan-m/ECC · error
Failed to save package manager config to
Error message
Failed to save package manager config to ${configPath}: ${err.message} What it means
After building the project config object, setProjectPackageManager writes it to <projectDir>/.claude/package-manager.json via writeFile; any write failure is rethrown as 'Failed to save package manager config to <path>: <reason>'. This keeps the original I/O error message attached to a descriptive wrapper.
Solutions
- Read the wrapped err.message for the root cause (ENOENT/EACCES/ENOSPC).
- Create the directory first: fs.mkdirSync(path.join(projectDir, '.claude'), { recursive: true }) then retry setProjectPackageManager.
- Ensure projectDir points at the actual project root and the current user has write permission.
- In CI, run in a writable workspace or persist the choice another way if the repo is read-only.
Example fix
// before
setProjectPackageManager('pnpm', projectDir); // throws ENOENT
// after
fs.mkdirSync(path.join(projectDir, '.claude'), { recursive: true });
setProjectPackageManager('pnpm', projectDir); Defensive patterns
Strategy: try-catch
Validate before calling
import fs from 'node:fs';
import path from 'node:path';
const dir = path.join(projectDir, '.claude');
fs.mkdirSync(dir, { recursive: true });
fs.accessSync(dir, fs.constants.W_OK); Type guard
null
Try / catch
try {
setProjectPackageManager('pnpm', projectDir);
} catch (e) {
if (e.message.startsWith('Failed to save package manager config')) {
// message includes configPath + cause (ENOENT/EACCES/ENOSPC); remediate and retry
console.error(e.message);
} else throw e;
} Prevention
- Create the .claude directory with recursive:true before project config writes
- Confirm projectDir is the real project root and writable
- Handle read-only workspaces in CI by persisting config elsewhere
- Include the failing path in your own error reporting for faster diagnosis
When it happens
Trigger: The .claude directory does not exist (ENOENT since writeFile doesn't mkdir), the project directory is read-only, path permissions deny the write (EACCES), or disk is full (ENOSPC).
Common situations: Fresh clones with no .claude directory yet; project dirs mounted read-only in containers; wrong projectDir argument pointing to a non-writable location; running as a user without write access to the repo.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Failed to save package manager preference
- gate.variant_missing
- Install manifests not found under
- memory connector path is not a directory
- agent profile inheritance cycle
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/88d4d4c59d34aa5e.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/lib/package-manager.js:281
* 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,
setAt: new Date().toISOString()
};
try {
writeFile(configPath, JSON.stringify(config, null, 2));
} catch (err) {
throw new Error(`Failed to save package manager config to ${configPath}: ${err.message}`);
}
return config;
}
// Allowed characters in script/binary names: alphanumeric, dash, underscore, dot, slash, @
// This prevents shell metacharacter injection while allowing scoped packages (e.g., @scope/pkg)
const SAFE_NAME_REGEX = /^[@a-zA-Z0-9_./-]+$/;
/**
* Get the command to run a script
* @param {string} script - Script name (e.g., "dev", "build", "test")
* @param {object} options - { projectDir }
* @throws {Error} If script name contains unsafe characters
*/
function getRunCommand(script, options = {}) {
if (!script || typeof script !== 'string') {
throw new Error('Script name must be a non-empty string');
}View on GitHub (pinned to 8321021c54)