affaan-m/ECC · error

Missing source file for repair

Error message

Missing source file for repair: ${sourcePath || operation.sourceRelativePath}

What it means

hydrateRecordedOperations rehydrates recorded update-claude-settings operations from install-state for a repair run. It resolves the operation's source file relative to the repo root and requires that it exists, since the repair needs the canonical source content to rebuild managed settings. If resolution fails or the file is missing, it throws, naming either the resolved path or the operation's sourceRelativePath.

Solutions

  1. Restore the missing source file (re-clone/update the ECC repo at the same path used during install) so sourceRelativePath resolves.
  2. Run repair from the correct repo root matching the recorded sourceRelativePath.
  3. If the source was intentionally removed, clean the stale install-state record instead of repairing it (reinstall from scratch).
  4. Check the error's path value and verify the file exists with that exact relative path under the repo root.

Example fix

// before: sourceRelativePath 'manifests/settings.json' deleted in repo
# after: restore the file from git
git checkout -- manifests/settings.json
# then re-run repair
Defensive patterns

Strategy: validation

Validate before calling

const fs = require('fs');
const path = require('path');
function assertRepairSourcesPresent(repoRoot, operations) {
  for (const op of operations.filter(o => o.kind === 'update-claude-settings')) {
    const p = op.sourceRelativePath && path.join(repoRoot, op.sourceRelativePath);
    if (!p || !fs.existsSync(p)) {
      throw new Error(`Repair source missing in repo: ${p || op.sourceRelativePath}`);
    }
  }
}

Type guard

function repairSourceExists(repoRoot, op) {
  const p = op.sourceRelativePath && path.join(repoRoot, op.sourceRelativePath);
  return Boolean(p) && fs.existsSync(p);
}

Try / catch

try {
  await repairManagedHooks();
} catch (err) {
  if (/Missing source file for repair/.test(err.message)) {
    console.error('Update/restore the ECC checkout matching the install-state, then re-run repair.');
  } else throw err;
}

Prevention

When it happens

Trigger: Running repair (repairManagedHooks) with install-state containing an update-claude-settings operation whose sourceRelativePath no longer resolves to an existing file under repoRoot — e.g. the settings template/source file was deleted or renamed, resolveOperationSourcePath returns null, or repair is run from the wrong repo root.

Common situations: Repairing an install after the plugin checkout was moved, renamed, or partially deleted; a version upgrade renamed the settings source file; running repair from a different working directory than the original install repo.

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16). Data as JSON: /api/errors/5ce4edb967a772f4. Report an issue: GitHub.

Appendix: source

Thrown at scripts/lib/install-lifecycle.js:734

      }
    }

    return Object.keys(nextValue).length === 0 ? JSON_REMOVE_SENTINEL : nextValue;
  }

  if (Array.isArray(managedValue)) {
    return jsonContainsSubset(currentValue, managedValue) ? JSON_REMOVE_SENTINEL : currentValue;
  }

  return currentValue === managedValue ? JSON_REMOVE_SENTINEL : currentValue;
}

function hydrateRecordedOperations(repoRoot, operations, trustedRoot) {
  return operations.map(operation => {
    if (operation.kind === 'update-claude-settings') {
      const sourcePath = resolveOperationSourcePath(repoRoot, operation);
      if (!sourcePath || !fs.existsSync(sourcePath)) {
        throw new Error(
          `Missing source file for repair: ${sourcePath || operation.sourceRelativePath}`
        );
      }
      return {
        ...operation,
        sourcePath,
        previousManagedHooks: operation.managedHooks,
        managedHooks: materializeManagedHooks(
          readHooksConfigNoFollow(sourcePath),
          trustedRoot
        ),
      };
    }

    if (operation.kind !== 'copy-file') {
      return { ...operation };
    }

View on GitHub (pinned to 8321021c54)