affaan-m/ECC · error · Error

Issue #${issue.number} is not open

Error message

Issue #${issue.number} is not open

What it means

Thrown by assertIssueClaimable() in scripts/lib/github-coordination/state.js when an attempt is made to claim a GitHub issue whose state is not 'open' (case-insensitive). The coordination workflow only allows claiming open issues.

Source

Thrown at scripts/lib/github-coordination/state.js:211

    action,
    status: state.status,
    owner: state.owner || null,
    branch: state.branch || null,
    validation: state.validation || 'pending',
    review: state.review || 'not-requested',
    project: summarizeProjectProjection(state, policy),
    dependencies: Array.isArray(state.dependencies) ? state.dependencies : [],
    tasks: Array.isArray(state.tasks) ? state.tasks : [],
    labels: normalizeLabels(issue.labels),
    workItemId: `github-${slugifySegment(repo)}-epic-${issue.number}`,
    lastActionAt: state.lastActionAt || null,
    lastSyncAt: state.lastSyncAt || null,
  };
}

function assertIssueClaimable(issue, state) {
  if (String(issue.state || '').toLowerCase() !== 'open') {
    throw new Error(`Issue #${issue.number} is not open`);
  }

  if (state.status === 'claimed') {
    throw new Error(`Issue #${issue.number} is already claimed by ${state.owner || 'unknown'}`);
  }
}

function verifyDependenciesClosed(repo, dependencyNumbers, options = {}, allIssues = null) {
  if (!Array.isArray(dependencyNumbers) || dependencyNumbers.length === 0) {
    return [];
  }

  const issueList = allIssues || listIssues(repo, { ...options, state: 'all', limit: options.limit || 200 });
  const closed = [];
  for (const dependencyNumber of dependencyNumbers) {
    const issue = findIssueByNumber(issueList, dependencyNumber);
    if (!issue) {
      process.stderr.write(`[github-coordination] Warning: dependency issue #${dependencyNumber} not found in issue list (may be in a different repo or beyond limit)\n`);

View on GitHub (pinned to 01e15490f0)

Solutions

  1. Re-fetch the issue to confirm its current state: gh issue view <number> --repo <repo> --json state,number.
  2. If the issue is genuinely closed, pick a different open issue or reopen it (with maintainer approval) before claiming.
  3. Refresh the local issue list (listIssues) before retrying the claim to avoid stale state.
  4. Guard the claim call with a pre-check on issue.state === 'open'.

Example fix

// before
assertIssueClaimable(issue, state);

// after
if (String(issue.state || '').toLowerCase() !== 'open') {
  throw new Error(`Cannot claim #${issue.number}: issue is ${issue.state}`);
}
assertIssueClaimable(issue, state);
Defensive patterns

Strategy: validation

Validate before calling

function isOpenIssue(issue) {
  return Boolean(issue) && String(issue.state || '').toLowerCase() === 'open';
}
if (!isOpenIssue(issue)) {
  throw new Error(`Refusing to act: #${issue && issue.number} is ${issue && issue.state}`);
}

Type guard

function isOpenIssue(issue) {
  return issue != null && typeof issue === 'object'
    && String(issue.state || '').toLowerCase() === 'open';
}

Try / catch

try {
  assertIssueClaimable(issue, state);
} catch (err) {
  if (/is not open/.test(err.message)) {
    return { skipped: true, reason: 'issue-closed' };
  }
  throw err;
}

Prevention

When it happens

Trigger: assertIssueClaimable(issue, state) is invoked with an issue object whose issue.state (lowercased) is 'closed' or any value other than 'open'. Common entry points: claim commands, sync workflows that re-check claimability.

Common situations: Issue was closed by a maintainer after the local cache was built; a stale issue number was passed; the issue was converted to a discussion; race condition where another agent closed it between fetch and claim.

Related errors


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