From 56c5b8d85280d1829d29a4f1bd820fbdb84a24b5 Mon Sep 17 00:00:00 2001 From: Naheel Muhammed Date: Sat, 30 May 2026 23:28:10 +0530 Subject: [PATCH 1/4] 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 5afa7398800d64905ca70c24ea7ee432f8ea8dde Mon Sep 17 00:00:00 2001 From: Naheel Muhammed Date: Sat, 30 May 2026 23:45:51 +0530 Subject: [PATCH 2/4] 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 762b11b7d84c6af11d41041c47930c3412e0972d 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 3/4] [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 889acedb76e6b729a174629514962c91e591f2bb Mon Sep 17 00:00:00 2001 From: Charles OpenClaw Date: Sat, 30 May 2026 20:01:23 +0000 Subject: [PATCH 4/4] fix: resolve branch commit to tree SHA --- src/lib/octokit.ts | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/lib/octokit.ts b/src/lib/octokit.ts index dfb00ea..76bd54c 100644 --- a/src/lib/octokit.ts +++ b/src/lib/octokit.ts @@ -173,9 +173,21 @@ export async function getRepoSnapshot( return typeof cur === "string" ? cur : undefined; }; - treeSha = - getNestedString(branch, ["commit", "commit", "tree", "sha"]) || - getNestedString(branch, ["commit", "sha"]); + treeSha = getNestedString(branch, ["commit", "commit", "tree", "sha"]); + + if (!treeSha) { + const commitSha = getNestedString(branch, ["commit", "sha"]); + + if (commitSha) { + const { data: commit } = await client.rest.git.getCommit({ + owner, + repo, + commit_sha: commitSha, + }); + + treeSha = commit.tree.sha; + } + } } catch { // If resolving the branch fails, fall back to the previous behavior treeSha = undefined;