googleapis / googleapis/release-please

addIssueLabels should retry on 422 "Issue not found" after PR creation

Open
#2,754 3 comments 2 reactions 1 assignee Claimed by @chingor13 View on GitHub
priority: p3 type: bug
Dominant language
TypeScript
Stars
7.5k
Forks
588
Avg merge
12h 16m
Merged PRs (30d)
7

Description

## Bug

`addIssueLabels` in `src/github-api.ts` has no retry logic for transient 422 errors. When release-please creates a PR and immediately adds the `autorelease: pending` label, the label call can fail with:

```
Validation Failed: {"resource":"Label","code":"unprocessable","field":"data","message":"Issue not found"}
```

This is a GitHub API eventual-consistency issue: the PR was created successfully, but isn't yet queryable by the Issues API on the replica that handles the label request.

## Impact

When the label addition fails, the entire release-please run fails. The PR exists but has no `autorelease: pending` label. When that PR is subsequently merged, the next release-please run cannot identify it as a release PR (it looks for merged PRs with `autorelease: pending`), so:

- No GitHub Release is created
- No tag is pushed
- Any downstream deployment pipelines that trigger on tag push never run
- release-please enters PR-creation mode and creates a new release PR with a potentially incorrect changelog (because the tag for the previous version doesn't exist)

The broken state is self-perpetuating and silent -- there are no warnings, and the new release PR masks the problem.

## Root Cause in Code

`addIssueLabels` ([github-api.ts:778](https://github.com/googleapis/release-please/blob/main/src/github-api.ts#L778)) is wrapped with `wrapAsync`, which provides no retry logic:

```typescript
addIssueLabels = wrapAsync(
async (labels: string[], number: number): Promise => {
// ...
await this.octokit.issues.addLabels({
owner: this.repository.owner,
repo: this.repository.repo,
issue_number: number,
labels,
});
}
);
```

By contrast, `graphqlRequest` (line 247) has retry logic with exponential backoff for 502 errors. `addIssueLabels` has none.

## Suggested Fix

Add retry logic to `addIssueLabels` for 422 errors where the message is "Issue not found". A simple retry with a short delay (e.g., 1-2 seconds, up to 3 attempts) would handle the eventual-consistency window. For example:

```typescript
addIssueLabels = wrapAsync(
async (labels: string[], number: number): Promise => {
if (labels.length === 0) return;
this.logger.debug(`adding labels: ${labels} to issue/pull ${number}`);
let lastError: Error | undefined;
for (let attempt = 0; attempt < 3; attempt++) {
try {
await this.octokit.issues.addLabels({
owner: this.repository.owner,
repo: this.repository.repo,
issue_number: number,
labels,
});
return;
} catch (e) {
const err = e as GitHubAPIError;
if (err.status === 422 && JSON.stringify(err).includes('Issue not found')) {
lastError = err;
const delay = (attempt + 1) * 1000;
this.logger.warn(`Label addition failed with 422 (attempt ${attempt + 1}/3), retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw e;
}
}
throw lastError;
}
);
```

The same pattern could be applied to `removeIssueLabels` which has the same vulnerability.

## Reproduction

This is a race condition and not deterministic. It manifests when the GitHub API's eventual consistency window is longer than the time between `createPullRequest` and `addIssueLabels` (typically milliseconds). We observed it in a production environment on Apr 13, 2026.

## Environment

- `release-please` version: latest (via `googleapis/release-please-action@v4`)
- GitHub: github.com (not GHES)
- Token type: GitHub App (via `peter-murray/workflow-application-token-action@v4`)

## Related

- googleapis/release-please-action#1105 -- reported as a permissions issue, but the same error message can also occur due to this race condition even when `issues: write` permission is correctly configured

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.