affaan-m/ECC · error · Error
Invalid ECC repo root: missing install script at ${installAp
Error message
Invalid ECC repo root: missing install script at ${installApplyPath} What it means
Every element of the changedFiles array must be a string path. A non-string entry (e.g. { path: '...' }) would stringify to '[object Object]' when joined into the security-trigger haystack, silently poisoning the security scan with garbage. The gate rejects the whole payload rather than shipping a corrupted haystack.
Source
Thrown at scripts/auto-update.js:140
}
// Recognized ECC package names. A repo root is only trusted to run its
// install-apply.js if its package.json identifies it as ECC — otherwise a
// cloned project that ships a nested `evil/{package.json,scripts/install-apply.js}`
// could drive auto-update into executing attacker code (GHSA-hfpv-w6mp-5g95).
const ECC_PACKAGE_NAMES = new Set(['ecc-universal', 'everything-claude-code']);
function validateRepoRoot(repoRoot) {
const normalized = path.resolve(repoRoot);
const packageJsonPath = path.join(normalized, 'package.json');
const installApplyPath = path.join(normalized, 'scripts', 'install-apply.js');
if (!fs.existsSync(packageJsonPath)) {
throw new Error(`Invalid ECC repo root: missing package.json at ${packageJsonPath}`);
}
if (!fs.existsSync(installApplyPath)) {
throw new Error(`Invalid ECC repo root: missing install script at ${installApplyPath}`);
}
let pkgName = null;
try {
pkgName = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')).name;
} catch {
throw new Error(`Invalid ECC repo root: unreadable package.json at ${packageJsonPath}`);
}
if (!ECC_PACKAGE_NAMES.has(pkgName)) {
throw new Error(`Refusing to run install from untrusted repo root ${normalized}: package.json name '${pkgName}' is not an official ECC package.`);
}
return normalized;
}
function runExternalCommand(command, args, options = {}) {
const result = spawnSync(command, args, {
cwd: options.cwd,View on GitHub (pinned to 01e15490f0)
Solutions
- Map objects to their path string before passing: changedFiles: files.map(f => f.path).
- Filter out nullish entries: changedFiles: files.filter(Boolean).
- Add a guard: if (!changedFiles.every(f => typeof f === 'string')) throw in your caller first.
Example fix
// before
orchReview({ diff, changedFiles: files }); // files = [{ path: 'a.js' }, ...]
// after
orchReview({ diff, changedFiles: files.map(f => f.path).filter(Boolean) }); Defensive patterns
Strategy: type-guard
Validate before calling
// Map file objects to string paths and filter nulls before calling.
const changedFiles = rawFiles
.map(f => (typeof f === 'string' ? f : f?.path))
.filter(f => typeof f === 'string' && f.length > 0);
orchReview({ diff, changedFiles }); Type guard
function isStringArray(v) {
return Array.isArray(v) && v.every(x => typeof x === 'string');
} Prevention
- When mapping file objects, always extract the string path field explicitly.
- Filter out null/undefined entries before passing the array.
- Run .every(f => typeof f === 'string') in your caller as a cheap precondition.
When it happens
Trigger: Passing changedFiles: [{ path: 'a.js' }, { path: 'b.js' }] (objects); changedFiles: ['a.js', 42, null] (mixed types); a .map step that returns the original object instead of the path field.
Common situations: Caller maps a list of file objects from git or an API and forgets to extract the string path; a null/undefined slips into the array from an optional field.
Related errors
- Invalid ECC repo root: missing package.json at ${packageJson
- Unknown argument: ${arg}
- ECC_PROJECT_DIR must be a child path within /workspace.
- Unable to infer ECC repo root from install-state operations
- TypeScript compiler not found. Install root dev dependencies
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/df867170f7895525.
Report an issue: GitHub.