affaan-m/ECC · error · ClaudeSetupError

(fail) ClaudeSetupError with code, message and details

Error message

(fail) ClaudeSetupError with code, message and details

What it means

fail() is the central error constructor for claude-plugin-setup.js: it throws a ClaudeSetupError carrying a machine-readable code, human message, and details object. Every failure path in this module (JSON parsing, plugin/marketplace listing, git availability, running the claude CLI, reading settings) funnels through it, so all setup failures share one shape.

Solutions

  1. Read error.code and error.details on the caught ClaudeSetupError — they identify the exact failing step and underlying stderr.
  2. Install prerequisites: ensure `claude` and `git` are on PATH (assertGitAvailable fails with a git code otherwise).
  3. If readSettings/parseJsonArray failed, fix or restore the malformed settings/marketplace JSON files named in details.
  4. Update the Claude Code CLI if the output shape changed, then re-run setup.

Example fix

// before
try { await setup(); } catch (e) { console.log(e.message); }
// after
try { await setup(); } catch (e) {
  if (e instanceof ClaudeSetupError) console.error(`[${e.code}]`, e.message, e.details);
  else throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

const { execSync } = require('child_process');
for (const bin of ['claude', 'git']) {
  try { execSync(`${bin} --version`, { stdio: 'ignore' }); } catch { throw new Error(`${bin} is not installed/on PATH`); }
}

Type guard

function isClaudeSetupError(e) { return e && typeof e.code === 'string' && typeof e.message === 'string' && e.details !== undefined; }

Try / catch

try { await runSetup(); } catch (e) {
  if (isClaudeSetupError(e)) {
    switch (e.code) { case 'git_unavailable': console.error('Install git'); break; default: console.error(e.code, e.message, e.details); }
  } else throw e;
}

Prevention

When it happens

Trigger: Running the plugin setup flow when: claude --plugin/--marketplace list output isn't a JSON array (parseJsonArray), the plugin/marketplace list command fails (parsePluginList/parseMarketplaceList), git isn't installed (assertGitAvailable), `claude` exits non-zero (runClaude), or settings.json can't be read/parsed (readSettings).

Common situations: Claude Code CLI not installed or not on PATH; git missing in slim CI images; corrupted or hand-edited settings.json; the `claude` CLI output format changed across versions, breaking JSON parsing.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

Thrown at scripts/lib/claude-plugin-setup.js:53

    this.observedScopes = [...(details.observedScopes || [])];
    this.recovery = [...(details.recovery || [])];
  }

  toJSON() {
    return {
      error: {
        code: this.code,
        message: this.message,
        phase: this.phase,
        observedScopes: [...this.observedScopes],
        recovery: [...this.recovery],
      },
    };
  }
}

function fail(code, message, details) {
  throw new ClaudeSetupError(code, message, details);
}

function normalizeGitHubRepository(value) {
  if (typeof value !== 'string') return null;
  const normalized = value.trim().replace(/\.git$/i, '').replace(/\/+$/, '');
  const match = normalized.match(/^([^/]+\/[^/]+)$/);
  return match ? match[1].toLowerCase() : null;
}

function normalizeMarketplaceRepository(marketplace) {
  return marketplace?.source === 'github'
    ? normalizeGitHubRepository(marketplace.repo)
    : normalizeGitHubGitOrigin(marketplace?.url);
}

function isOfficialMarketplace(marketplace) {
  if (!marketplace || marketplace.name !== OFFICIAL_MARKETPLACE_NAME) return false;
  return normalizeMarketplaceRepository(marketplace) === OFFICIAL_MARKETPLACE_REPO;

View on GitHub (pinned to 8321021c54)