affaan-m/ECC · error · Error
Missing source file for repair: ${sourcePath || operation.so
Error message
Missing source file for repair: ${sourcePath || operation.sourceRelativePath} What it means
During a 'copy-file' repair operation the installer resolves the original source path from the install-state and checks it exists. The install-state file is treated as attacker-controllable (per GHSA-hfpv-w6mp-5g95), so a missing or unresolvable source is a hard failure rather than a fallback. Thrown by executeRepairOperation when resolveOperationSourcePath returns null/empty or fs.existsSync is false.
Source
Thrown at scripts/lib/install-lifecycle.js:601
repoVersion: context.packageVersion,
manifestVersion: context.manifestVersion
},
lastValidatedAt: new Date().toISOString()
};
}
function shouldRepairFromRecordedOperations(state) {
return getManagedOperations(state).some(operation => operation.kind !== 'copy-file');
}
function executeRepairOperation(repoRoot, operation, trustedRoot) {
// Install-state is attacker-controllable; never write/delete outside the
// adapter-derived trusted root, regardless of what the state file claims
// (GHSA-hfpv-w6mp-5g95).
if (operation.kind === 'copy-file') {
const sourcePath = resolveOperationSourcePath(repoRoot, operation);
if (!sourcePath || !fs.existsSync(sourcePath)) {
throw new Error(`Missing source file for repair: ${sourcePath || operation.sourceRelativePath}`);
}
copyContainedFile(sourcePath, operation.destinationPath, trustedRoot, 'repair');
return operation.destinationPath;
}
if (operation.kind === 'render-template') {
const renderedContent = getOperationTextContent(operation);
if (renderedContent === null) {
throw new Error(`Missing rendered content for repair: ${operation.destinationPath}`);
}
writeContainedFile(operation.destinationPath, renderedContent, trustedRoot, 'repair');
return operation.destinationPath;
}
if (operation.kind === 'merge-json') {
const payload = getOperationJsonPayload(operation);View on GitHub (pinned to 01e15490f0)
Solutions
- Verify the recorded source path exists: `ls <repoRoot>/<operation.sourceRelativePath>` (path is in the error message).
- Restore the missing file (checkout the branch/commit that owned it, or revert the prune).
- If the source was intentionally removed, uninstall first to clear the stale install-state, then re-install from the current repo state.
- Regenerate the install-state by performing a fresh install instead of repair.
Example fix
// before: repair fails — skills/foo/SKILL.md was deleted from repo // after: git checkout main -- skills/foo/SKILL.md ./install.sh --target claude --repair
Defensive patterns
Strategy: validation
Validate before calling
const fs = require('fs');
const path = require('path');
function verifyOperationSources(repoRoot, operations) {
const missing = [];
for (const op of operations) {
if (op.kind !== 'copy-file') continue;
const src = op.sourceRelativePath ? path.join(repoRoot, op.sourceRelativePath) : null;
if (!src || !fs.existsSync(src)) missing.push(src || op.sourceRelativePath);
}
return missing;
}
// before repair: assert verifyOperationSources(repoRoot, state.operations).length === 0 Type guard
function operationHasResolvableSource(repoRoot, op) {
if (op.kind !== 'copy-file') return true;
const src = op.sourceRelativePath ? path.join(repoRoot, op.sourceRelativePath) : null;
return Boolean(src && fs.existsSync(src));
} Try / catch
try {
plan = createRepairPlanFromRecord(record, ctx);
} catch (err) {
if (err.message.startsWith('Missing source file for repair')) {
// restore the source from git, or uninstall + reinstall instead of repair
} else throw err;
} Prevention
- Do not delete files from skills/, rules/, etc. that the installer copied without also uninstalling.
- Pin the repo to the same commit used at install time before repairing.
- Prefer re-install over repair when the source tree has changed substantially.
- Keep install-state under version control or backed up.
When it happens
Trigger: Running repair (`./install.sh --repair` or the repair plan path) when the recorded source file (operation.sourceRelativePath under repoRoot) has been deleted, moved, or was never committed. Also when the install-state's sourceRelativePath was hand-edited to point at a non-existent file.
Common situations: Repo was pruned (skills/ or rules/ subdir removed) between install and repair; user switched branches and the recorded source no longer exists; partial clone missing the file; install-state file manually edited or corrupted.
Related errors
- Refusing to read non-file path: ${filePath}
- Missing rendered content for repair: ${operation.destination
- Missing merge payload for repair: ${operation.destinationPat
- Unsupported repair operation kind: ${operation.kind}
- No install-state available for repair
AI-assisted analysis of affaan-m/ECC@01e15490f0 (2026-08-13).
Data as JSON: /api/errors/5ce4edb967a772f4.
Report an issue: GitHub.