From f6574b64f42eb4b3bdf32ca4388e6d39e19e8326 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 18:22:08 +0000 Subject: [PATCH 1/5] Initial plan From a78f91f3b28c07c2dbcd3eeb7152b6515a74e0e3 Mon Sep 17 00:00:00 2001 From: Naheel Muhammed Date: Sat, 30 May 2026 23:28:10 +0530 Subject: [PATCH 2/5] fix: clarify missing repository errors --- src/lib/octokit.ts | 53 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/src/lib/octokit.ts b/src/lib/octokit.ts index 91c00f4..9a09d50 100644 --- a/src/lib/octokit.ts +++ b/src/lib/octokit.ts @@ -4,6 +4,7 @@ type RepoAccessErrorCode = | "AUTH_REQUIRED" | "NOT_FOUND" | "FORBIDDEN" + | "RATE_LIMITED" | "UNKNOWN"; export class RepoAccessError extends Error { @@ -40,6 +41,56 @@ function toRepoAccessError( ? error.status : 500; + const responseHeaders = + typeof error === "object" && + error !== null && + "response" in error && + typeof error.response === "object" && + error.response !== null && + "headers" in error.response && + typeof error.response.headers === "object" && + error.response.headers !== null + ? error.response.headers + : undefined; + + const responseMessage = + typeof error === "object" && + error !== null && + "response" in error && + typeof error.response === "object" && + error.response !== null && + "data" in error.response && + typeof error.response.data === "object" && + error.response.data !== null && + "message" in error.response.data && + typeof error.response.data.message === "string" + ? error.response.data.message + : ""; + + const rateLimitRemaining = + responseHeaders && + "x-ratelimit-remaining" in responseHeaders && + responseHeaders["x-ratelimit-remaining"]; + + const retryAfter = + responseHeaders && + "retry-after" in responseHeaders && + responseHeaders["retry-after"]; + + if ( + status === 429 || + (status === 403 && + (String(rateLimitRemaining) === "0" || + retryAfter !== undefined || + responseMessage.toLowerCase().includes("rate limit"))) + ) { + return new RepoAccessError( + "GitHub API rate limit reached. Please wait a few minutes and try again.", + 429, + "RATE_LIMITED", + ); + } + if (status === 401) { return new RepoAccessError( "Access token expired or invalid. Please re-authenticate with GitHub.", @@ -58,7 +109,7 @@ function toRepoAccessError( if (status === 404) { return new RepoAccessError( - "Repository not found or you do not have access to it.", + "Repository not found. Please check the URL and try again.", 404, "NOT_FOUND", ); From 328d7cd91b726dceb61099d8b8593032b4e78169 Mon Sep 17 00:00:00 2001 From: Naheel Muhammed Date: Sat, 30 May 2026 23:45:51 +0530 Subject: [PATCH 3/5] fix(octokit): resolve default branch to tree SHA and improve error handling --- src/lib/octokit.ts | 57 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 13 deletions(-) diff --git a/src/lib/octokit.ts b/src/lib/octokit.ts index 9a09d50..0a3a269 100644 --- a/src/lib/octokit.ts +++ b/src/lib/octokit.ts @@ -50,7 +50,7 @@ function toRepoAccessError( "headers" in error.response && typeof error.response.headers === "object" && error.response.headers !== null - ? error.response.headers + ? (error.response.headers as Record) : undefined; const responseMessage = @@ -67,22 +67,24 @@ function toRepoAccessError( ? error.response.data.message : ""; - const rateLimitRemaining = - responseHeaders && - "x-ratelimit-remaining" in responseHeaders && - responseHeaders["x-ratelimit-remaining"]; + // Normalize header lookup to be case-insensitive + const getHeader = (name: string) => { + if (!responseHeaders) return undefined; + const found = Object.keys(responseHeaders).find( + (k) => k.toLowerCase() === name.toLowerCase(), + ); + return found ? responseHeaders[found] : undefined; + }; - const retryAfter = - responseHeaders && - "retry-after" in responseHeaders && - responseHeaders["retry-after"]; + const rateLimitRemaining = getHeader("x-ratelimit-remaining"); + const retryAfter = getHeader("retry-after"); if ( status === 429 || (status === 403 && (String(rateLimitRemaining) === "0" || retryAfter !== undefined || - responseMessage.toLowerCase().includes("rate limit"))) + (typeof responseMessage === "string" && responseMessage.toLowerCase().includes("rate limit")))) ) { return new RepoAccessError( "GitHub API rate limit reached. Please wait a few minutes and try again.", @@ -99,10 +101,10 @@ function toRepoAccessError( ); } - if ((status === 403 || status === 404) && !hasUserAccessToken) { + if (status === 403 && !hasUserAccessToken) { return new RepoAccessError( "Repository not accessible. It may be private or unavailable. Log in with GitHub if you need access to a private repository.", - 401, + 403, "AUTH_REQUIRED", ); } @@ -142,10 +144,39 @@ export async function getRepoSnapshot( repo, }); + // Resolve the branch to a tree SHA (default_branch is a name, not a SHA) + let treeSha: string | undefined = undefined; + try { + const { data: branch } = await client.rest.repos.getBranch({ + owner, + repo, + branch: repoInfo.default_branch, + }); + + const getNestedString = (obj: unknown, path: string[]): string | undefined => { + let cur: unknown = obj; + for (const p of path) { + if (typeof cur === "object" && cur !== null && p in (cur as Record)) { + cur = (cur as Record)[p]; + } else { + return undefined; + } + } + return typeof cur === "string" ? cur : undefined; + }; + + treeSha = + getNestedString(branch, ["commit", "commit", "tree", "sha"]) || + getNestedString(branch, ["commit", "sha"]); + } catch { + // If resolving the branch fails, fall back to the previous behavior + treeSha = undefined; + } + const { data: repoTree } = await client.rest.git.getTree({ owner, repo, - tree_sha: repoInfo.default_branch, + tree_sha: treeSha ?? repoInfo.default_branch, }); type RepoTreeItem = (typeof repoTree.tree)[number]; From 5d1411c248e27ca3806f1c8c2bd29290d18cf2f0 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Sat, 30 May 2026 18:18:10 +0000 Subject: [PATCH 4/5] [autofix.ci] apply automated fixes --- src/lib/octokit.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/lib/octokit.ts b/src/lib/octokit.ts index 0a3a269..dfb00ea 100644 --- a/src/lib/octokit.ts +++ b/src/lib/octokit.ts @@ -84,7 +84,8 @@ function toRepoAccessError( (status === 403 && (String(rateLimitRemaining) === "0" || retryAfter !== undefined || - (typeof responseMessage === "string" && responseMessage.toLowerCase().includes("rate limit")))) + (typeof responseMessage === "string" && + responseMessage.toLowerCase().includes("rate limit")))) ) { return new RepoAccessError( "GitHub API rate limit reached. Please wait a few minutes and try again.", @@ -153,10 +154,17 @@ export async function getRepoSnapshot( branch: repoInfo.default_branch, }); - const getNestedString = (obj: unknown, path: string[]): string | undefined => { + const getNestedString = ( + obj: unknown, + path: string[], + ): string | undefined => { let cur: unknown = obj; for (const p of path) { - if (typeof cur === "object" && cur !== null && p in (cur as Record)) { + if ( + typeof cur === "object" && + cur !== null && + p in (cur as Record) + ) { cur = (cur as Record)[p]; } else { return undefined; From 4bf73a09676e23c13c7dd16cd09cd26f6cfad030 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 30 May 2026 18:25:15 +0000 Subject: [PATCH 5/5] fix(octokit): remove extra branch lookup before tree fetch --- src/lib/octokit.ts | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/src/lib/octokit.ts b/src/lib/octokit.ts index dfb00ea..14baa09 100644 --- a/src/lib/octokit.ts +++ b/src/lib/octokit.ts @@ -145,46 +145,10 @@ export async function getRepoSnapshot( repo, }); - // Resolve the branch to a tree SHA (default_branch is a name, not a SHA) - let treeSha: string | undefined = undefined; - try { - const { data: branch } = await client.rest.repos.getBranch({ - owner, - repo, - branch: repoInfo.default_branch, - }); - - const getNestedString = ( - obj: unknown, - path: string[], - ): string | undefined => { - let cur: unknown = obj; - for (const p of path) { - if ( - typeof cur === "object" && - cur !== null && - p in (cur as Record) - ) { - cur = (cur as Record)[p]; - } else { - return undefined; - } - } - return typeof cur === "string" ? cur : undefined; - }; - - treeSha = - getNestedString(branch, ["commit", "commit", "tree", "sha"]) || - getNestedString(branch, ["commit", "sha"]); - } catch { - // If resolving the branch fails, fall back to the previous behavior - treeSha = undefined; - } - const { data: repoTree } = await client.rest.git.getTree({ owner, repo, - tree_sha: treeSha ?? repoInfo.default_branch, + tree_sha: repoInfo.default_branch, }); type RepoTreeItem = (typeof repoTree.tree)[number];