fix: clarify release creation 404 errors (#817)

* fix: clarify release creation 404 errors

Signed-off-by: Rui Chen <rui@chenrui.dev>

* fix: clarify inaccessible release targets

Signed-off-by: Rui Chen <rui@chenrui.dev>

---------

Signed-off-by: Rui Chen <rui@chenrui.dev>
This commit is contained in:
Rui Chen
2026-07-13 10:02:44 -04:00
committed by GitHub
parent e6c70a53cf
commit 7e13ed4ac5
4 changed files with 355 additions and 23 deletions
+48 -2
View File
@@ -68,6 +68,43 @@ type ReleaseMutationParams = {
previous_tag_name?: string;
};
class ReleaseCreationError extends Error {
readonly status = 404;
constructor(message: string, cause: unknown) {
super(message, { cause });
this.name = 'ReleaseCreationError';
}
}
class ReleaseAccessError extends Error {
readonly status = 404;
constructor(message: string, cause: unknown) {
super(message, { cause });
this.name = 'ReleaseAccessError';
}
}
const repositoryAccessGuidance = (owner: string, repo: string): string =>
`Verify that ${owner}/${repo} exists under the expected owner, the token can access it, the repository is selected when using a fine-grained PAT, and the token has Contents: write permission.`;
const releaseCreation404Message = (
owner: string,
repo: string,
discussionCategory: string | undefined,
error: unknown,
): string => {
const discussionGuidance = discussionCategory
? ` Also verify that Discussions and the requested category "${discussionCategory}" are enabled.`
: '';
return `GitHub returned 404 while creating the release. ${repositoryAccessGuidance(owner, repo)}${discussionGuidance} GitHub response: ${errorMessage(error)}`;
};
const releaseLookup404Message = (owner: string, repo: string, error: unknown): string =>
`GitHub returned 404 while checking existing releases. ${repositoryAccessGuidance(owner, repo)} GitHub response: ${errorMessage(error)}`;
export interface Releaser {
getReleaseByTag(params: { owner: string; repo: string; tag: string }): Promise<{ data: Release }>;
@@ -566,6 +603,11 @@ export const release = async (
try {
_release = await findTagFromReleases(releaser, owner, repo, tag, maxRetries);
} catch (error) {
if (error.status === 404) {
const diagnostic = releaseLookup404Message(owner, repo, error);
console.log(`⚠️ ${diagnostic}`);
throw new ReleaseAccessError(diagnostic, error);
}
console.log(
`⚠️ Unexpected error fetching GitHub release for tag ${config.github_ref}: ${error}`,
);
@@ -644,6 +686,9 @@ export const release = async (
created: false,
};
} catch (error) {
if (error instanceof ReleaseCreationError) {
throw error;
}
if (error.status !== 404) {
console.log(
`⚠️ Unexpected error fetching GitHub release for tag ${config.github_ref}: ${error}`,
@@ -1029,8 +1074,9 @@ async function createRelease(
throw error;
case 404:
console.log('Skip retry - discussion category mismatch');
throw error;
const diagnostic = releaseCreation404Message(owner, repo, discussion_category_name, error);
console.log(`Skip retry — ${diagnostic}`);
throw new ReleaseCreationError(diagnostic, error);
case 422:
// Check if this is a race condition with "already_exists" error