{"record":{"id":"54e8045438c94c2a","repo":"OtterMind/Chat2DB","slug":"failed-to-persist-cleanup-and-roll-back-assignment","errorCode":null,"errorMessage":"Failed to persist cleanup and roll back assignment changes.","messagePattern":"Failed to persist cleanup and roll back assignment changes\\.","errorType":"exception","errorClass":"AggregateError","httpStatus":null,"severity":"error","filePath":"script/github/issue-claim.js","lineNumber":691,"sourceCode":"    updatedAt: now,\n    reason,\n  };\n  const messages = {\n    issue_closed: `@${state.claimant}'s claim was released because the issue was closed.`,\n    task_unpublished: `@${state.claimant}'s claim was released because this is no longer a published contribution task.`,\n    assignment_removed: `@${state.claimant}'s claim was released after the assignment was removed.`,\n  };\n  try {\n    await client.upsertStateComment(\n      issue.number,\n      record,\n      renderStateComment(state, messages[reason], policy),\n    );\n  } catch (error) {\n    try {\n      await rollbackEffects(client, issue.number, appliedEffects);\n    } catch (rollbackError) {\n      throw new AggregateError(\n        [error, rollbackError],\n        'Failed to persist cleanup and roll back assignment changes.',\n      );\n    }\n    throw error;\n  }\n  return reason;\n}\n\nasync function main() {\n  const mode = process.argv[2];\n  const policyPath = process.env.CLAIM_POLICY_PATH\n    || path.resolve(process.cwd(), '.github/claim-policy.json');\n  const policy = loadPolicy(policyPath);\n  const client = new GitHubClient({\n    token: process.env.GITHUB_TOKEN,\n    repository: process.env.GITHUB_REPOSITORY,\n    apiUrl: process.env.GITHUB_API_URL,","sourceCodeStart":673,"sourceCodeEnd":709,"githubUrl":"https://github.com/OtterMind/Chat2DB/blob/5ee1e990e73fbcae1969dc554be254fedb3ab888/script/github/issue-claim.js#L673-L709","documentation":"This AggregateError is thrown in the claim-cleanup flow when two operations fail in sequence: first, upsertStateComment (persisting the released-claim state to a GitHub issue comment) fails, and then the rollback of appliedEffects (re-adding the removed assignee) also fails. The AggregateError bundles both errors so neither is lost. This is a defensive transactional pattern: it tries to undo the assignee removal when state persistence fails, and if the undo itself fails, it signals total inconsistency.","triggerScenarios":"Running issue-claim.js in 'cleanup' mode (or during 'event' processing that triggers cleanup) where: (1) the GitHub API call to upsert the state comment fails (rate limit, network, auth), AND (2) the subsequent rollback call to re-add the assignee via applyEffects also fails. Both must fail to produce this specific AggregateError.","commonSituations":"GitHub API rate limiting (403) causing both the comment and the assignee restore to fail. GITHUB_TOKEN expired or revoked between calls. Network outage during the cleanup transaction. Repository permissions changed mid-run.","solutions":["Inspect error.errors array (the AggregateError exposes both the original and rollback errors) to identify the root API failure.","Check GITHUB_TOKEN validity and repository write permissions.","Check GitHub API rate limit status (X-RateLimit-Remaining headers).","Re-run the cleanup after resolving the API access issue; the script is idempotent for state transitions."],"exampleFix":"// before\nawait client.upsertStateComment(issue.number, record, body);\n\n// after\ntry {\n  await client.upsertStateComment(issue.number, record, body);\n} catch (e) {\n  if (e instanceof AggregateError) {\n    for (const inner of e.errors) {\n      console.error('Cleanup failure detail:', inner);\n    }\n    // alert operator for manual reconciliation\n  }\n  throw e;\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"function isAggregateError(e: unknown): e is AggregateError {\n  return e instanceof AggregateError && Array.isArray(e.errors);\n}","tryCatchPattern":"try {\n  await cleanupClaim({ client, policy, event });\n} catch (e) {\n  if (e instanceof AggregateError) {\n    for (const inner of e.errors) {\n      console.error('Cleanup failure detail:', inner?.message ?? inner);\n    }\n    // manual reconciliation required — state may be inconsistent\n    process.exitCode = 2;\n  } else {\n    throw e;\n  }\n}","preventionTips":["Ensure GITHUB_TOKEN has issue:write and repo permissions before running cleanup.","Monitor GitHub API rate limits before running batch claim operations.","Run claim cleanup in an idempotent workflow that can be safely retried.","Alert operators when this AggregateError occurs so they can reconcile assignee state manually."],"tags":["github","automation","transaction","claim-management","node"],"backgroundTag":null,"analyzedSha":"5ee1e990e73fbcae1969dc554be254fedb3ab888","analyzedAt":"2026-08-14T07:05:03.077Z","schemaVersion":2},"datasetVersion":"2026-08-14T10:17:34.591Z"}