affaan-m/ECC · error · Error

Failed to read ${label}: ${error.message}

Error message

Failed to read ${label}: ${error.message}

What it means

readJson wraps fs.readFileSync + JSON.parse for the manifest files (install-modules.json, install-profiles.json, install-components.json). Any filesystem error (missing file, permission denied) or JSON syntax error is rethrown with the caller-supplied label prefixed, so the caller knows which manifest failed.

Source

Thrown at scripts/lib/install-manifests.js:152

});
const TARGET_DEFAULT_PROFILE_IDS = Object.freeze({
  opencode: 'opencode',
});
const TARGET_DEFAULT_EXCLUSIONS = Object.freeze({
  opencode: [
    {
      moduleId: 'hooks-runtime',
      reason: 'OpenCode defaults intentionally exclude hooks-runtime until users opt in.',
      optInCommand: './install.sh --target opencode --modules hooks-runtime',
    },
  ],
});

function readJson(filePath, label) {
  try {
    return JSON.parse(fs.readFileSync(filePath, 'utf8'));
  } catch (error) {
    throw new Error(`Failed to read ${label}: ${error.message}`);
  }
}

function dedupeStrings(values) {
  return [...new Set((Array.isArray(values) ? values : []).map(value => String(value).trim()).filter(Boolean))];
}

function listSkillDirectoryIds(repoRoot) {
  const skillsRoot = path.join(repoRoot, 'skills');
  if (!fs.existsSync(skillsRoot) || !fs.statSync(skillsRoot).isDirectory()) {
    return [];
  }

  return fs.readdirSync(skillsRoot, { withFileTypes: true })
    .filter(entry => entry.isDirectory())
    .map(entry => entry.name)
    .sort();
}

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Open the manifest named by `${label}` and locate the JSON error (line/column from the underlying error message).
  2. Restore the file from git: `git checkout HEAD -- manifests/<file>.json`.
  3. Run the installer from the repository root so DEFAULT_REPO_ROOT (__dirname/../..) points at manifests/.
  4. Fix filesystem permissions: `chmod -R u+rw manifests/`.

Example fix

// before: manifests/install-modules.json has a trailing comma → parse fails
// after: validate then restore
node -e 'JSON.parse(require("fs").readFileSync("manifests/install-modules.json","utf8"))'
git checkout HEAD -- manifests/install-modules.json
Defensive patterns

Strategy: try-catch

Validate before calling

const fs = require('fs');
function manifestsAreReadable(repoRoot) {
  for (const name of ['install-modules.json', 'install-profiles.json', 'install-components.json']) {
    const p = `${repoRoot}/manifests/${name}`;
    if (!fs.existsSync(p)) continue; // components optional
    JSON.parse(fs.readFileSync(p, 'utf8'));
  }
  return true;
}
// before invoking installer: manifestsAreReadable(repoRoot)

Type guard

function isParsableJsonFile(filePath) {
  try { JSON.parse(fs.readFileSync(filePath, 'utf8')); return true; } catch { return false; }
}

Try / catch

try {
  return readJson(filePath, label);
} catch (err) {
  if (err.message.startsWith(`Failed to read ${label}`)) {
    // restore the manifest from git or fail gracefully
  } else throw err;
}

Prevention

When it happens

Trigger: loadInstallManifests or any manifest reader calling readJson when the manifest file does not exist at the expected path, is not readable, or contains malformed JSON. The label in the message identifies which of the three manifest files failed.

Common situations: Running the installer from a shallow clone that excluded manifests/; manifests/ files were deleted or git-ignored; hand-edited manifest introduced a JSON syntax error; permission/ownership issue on the manifest file; running installer from the wrong working directory so DEFAULT_REPO_ROOT resolves wrong.

Related errors


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