{"record":{"id":"5ef6d29f7cb12cf2","repo":"coleam00/Archon","slug":"authentication-failed-for-owner-repo-auth","errorCode":null,"errorMessage":"Authentication failed for ${owner}/${repo}. ${authHint}","messagePattern":"Authentication failed for (.+?)/(.+?)\\. (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/adapters/src/forge/github/adapter.ts","lineNumber":766,"sourceCode":"      ghToken ? { token: ghToken } : undefined\n    );\n\n    if (!cloneResult.ok) {\n      getLog().error(\n        { error: cloneResult.error, owner, repo, repoPath },\n        'github.repo_clone_failed'\n      );\n\n      if (cloneResult.error.code === 'not_a_repo') {\n        throw new Error(\n          `Repository ${owner}/${repo} not found or is private. Check repository access.`\n        );\n      } else if (cloneResult.error.code === 'permission_denied') {\n        const authHint =\n          this.auth.kind === 'app'\n            ? 'Check that the Archon GitHub App is installed on the org and has the Contents:Read permission.'\n            : 'Check GITHUB_TOKEN permissions.';\n        throw new Error(`Authentication failed for ${owner}/${repo}. ${authHint}`);\n      }\n      throw new Error(\n        `Failed to clone ${owner}/${repo}: ${'message' in cloneResult.error ? cloneResult.error.message : cloneResult.error.code}`\n      );\n    }\n\n    await addSafeDirectory(toRepoPath(repoPath));\n\n    // App mode: install the git credential helper on the newly cloned worktree\n    // so workflows that outlive the 1h installation-token expiry can refresh\n    // credentials in-place. Non-fatal — workflows that complete in <1h still\n    // succeed via the URL-embedded token from the clone above. The result\n    // discriminator tells us whether the install actually happened so we\n    // don't log a false \"installed\" line in builds where the helper script\n    // isn't on disk.\n    if (this.auth.kind === 'app') {\n      const result = await installCredentialHelper(repoPath);\n      switch (result.kind) {","sourceCodeStart":748,"sourceCodeEnd":784,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/adapters/src/forge/github/adapter.ts#L748-L784","documentation":"Thrown by GitHubAdapter.ensureRepoReady when cloneRepository returns code 'permission_denied': the repository exists but the credentials used for the clone were rejected or lack read access. The message includes an auth-specific hint depending on whether Archon is running in GitHub App mode or PAT mode (GITHUB_TOKEN).","triggerScenarios":"handleWebhook -> ensureRepoReady clones with an installation token or GITHUB_TOKEN/GH_TOKEN that is invalid, expired, or lacks Contents:Read on the target repo.","commonSituations":"Expired/rotated GITHUB_TOKEN; fine-grained PAT not granted to the repo; GitHub App installed org-wide but without Contents:Read permission; token scoped to a different org; app installed on the org but the repo excluded.","solutions":["In App mode: verify the Archon GitHub App is installed on the org and granted Contents (Read) permission, then retry so a fresh ~1h installation token is minted.","In PAT mode: check GITHUB_TOKEN/GH_TOKEN is set, unexpired, and has read access to the repository (classic PAT with 'repo' scope or fine-grained PAT covering the repo).","Test the credential manually: git clone https://github.com/${owner}/${repo}.git with the same token to reproduce the denial.","Confirm app installation includes the specific repository (not just selected repos that exclude it)."],"exampleFix":"// before\nexport GITHUB_TOKEN=ghp_expired_token\n// after: fresh token with repo read access\nexport GITHUB_TOKEN=github_pat_11AAAA..._with_contents_read","handlingStrategy":"validation","validationCode":"// Check the token can read the repo before invoking adapter flows\nconst res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {\n  headers: { Authorization: `Bearer ${process.env.GITHUB_TOKEN}` },\n});\nif (!res.ok) throw new Error(`Token cannot read ${owner}/${repo}: HTTP ${res.status}`);","typeGuard":null,"tryCatchPattern":"try {\n  await adapter.handleWebhook(payload);\n} catch (err) {\n  if (err instanceof Error && err.message.startsWith('Authentication failed for')) {\n    // rotate token / verify App installation before retrying\n  }\n}","preventionTips":["Rotate GITHUB_TOKEN before expiry and grant Contents:Read (classic: repo scope).","Verify App installation includes each repo and Contents:Read permission.","Smoke-test the clone with the same credential in CI.","Prefer App mode so tokens auto-expire/refresh (~1h installation tokens)."],"tags":["github","authentication","clone","permissions"],"backgroundTag":"git-authentication-failed","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}