{"record":{"id":"42bc43cf702d64ad","repo":"unslothai/unsloth","slug":"github-endpoint-returned-r-status-code-r-reas","errorCode":null,"errorMessage":"GitHub {endpoint} returned {r.status_code} {r.reason}. Token source: {self._token_source}. The token is invalid, expired, or missing required scopes — retrying will not recover.{request_id_message} Response: {snippet}","messagePattern":"GitHub (.+?) returned (.+?) (.+?)\\. Token source: (.+?)\\. The token is invalid, expired, or missing required scopes — retrying will not recover\\.(.+?) Response: (.+?)","errorType":"exception","errorClass":"GitHubAuthError","httpStatus":null,"severity":"critical","filePath":"studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/scraper_impl/gh_client.py","lineNumber":128,"sourceCode":"        )\n\n    def _is_auth_failure(self, r: \"requests.Response\") -> bool:\n        \"\"\"Tell auth failures apart from rate limiting on 401/403.\n\n        401 is always auth; 403 is auth unless it carries a rate-limit signal\n        (Retry-After, X-RateLimit-Remaining: 0, or abuse/secondary text).\n        \"\"\"\n        if r.status_code == 401:\n            return True\n        if r.status_code == 403:\n            return not self._is_rate_limit_response(r)\n        return False\n\n    def _raise_auth_error(self, r: \"requests.Response\", endpoint: str) -> None:\n        snippet = (r.text or \"\").strip()[:200]\n        request_id = r.headers.get(\"X-GitHub-Request-Id\")\n        request_id_message = f\" Request ID: {request_id}.\" if request_id else \"\"\n        raise GitHubAuthError(\n            f\"GitHub {endpoint} returned {r.status_code} {r.reason}. \"\n            f\"Token source: {self._token_source}. \"\n            f\"The token is invalid, expired, or missing required scopes — \"\n            f\"retrying will not recover.{request_id_message} Response: {snippet}\"\n        )\n\n    def _check_rate_and_wait(self, kind: str) -> None:\n        if kind == \"graphql\":\n            remaining = self.graphql_remaining\n            reset = self.graphql_reset\n            min_remaining = self.min_remaining_graphql\n        else:\n            remaining = self.rest_remaining\n            reset = self.rest_reset\n            min_remaining = self.min_remaining_rest\n        if remaining is not None and remaining < min_remaining:\n            if reset:\n                self._sleep_until(reset)","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/plugins/data-designer-github-repo-seed/src/data_designer_github_repo_seed/scraper_impl/gh_client.py#L110-L146","documentation":"GitHubAuthError raised by _raise_auth_error for GitHub responses where the client determined the failure is non-retryable: HTTP 401 always, and 403 only when it is NOT a rate-limit response (checked via Retry-After / X-RateLimit-Remaining: 0 / abuse detection text). The message includes the endpoint, status, token source, GitHub request ID, and first 200 bytes of the response body so the user can diagnose scope/expiry issues. Retrying will not help — the token itself is bad.","triggerScenarios":"Any GraphQL or REST call returning 401 (bad/expired/revoked token), or 403 for reasons other than rate limiting (SSO authorization missing, fine-grained PAT without access to the org/repo, missing scope like repo for a private repo).","commonSituations":"Token expired or revoked between runs; fine-grained PAT not granted to the target organization or lacking the 'contents:read' scope; organization requires SSO/SAML authorization the PAT never received; classic PAT without repo scope hitting private repos.","solutions":["Regenerate or extend the token and update GH_TOKEN/GITHUB_TOKEN (or the recipe token field).","For fine-grained PATs, grant access to the specific repos/orgs and required permissions (contents, issues, pull requests read).","For org repos, authorize the token for SSO/SAML in GitHub settings if the org enforces it.","Use the Request ID in the message when contacting GitHub support."],"exampleFix":"# before\nexport GH_TOKEN=\"ghp_expiredtoken...\"\n\n# after\n# create a fresh token with repo scope at https://github.com/settings/tokens\nexport GH_TOKEN=\"ghp_newtoken...\"","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"from data_designer_github_repo_seed.scraper_impl.gh_client import GitHubAuthError\n\ntry:\n    client.fetch_repo_issues(\"owner/name\")\nexcept GitHubAuthError as e:\n    msg = str(e)\n    if \"401\" in msg or \"403\" in msg:\n        # non-retryable: rotate/fix the token, check scopes/SSO, then re-run\n        report_fatal_auth(msg)  # includes token source + GitHub Request ID\n    raise","preventionTips":["Preflight the token with a cheap authenticated call (GET /user) before starting a long scrape.","Use fine-grained PATs granted to the exact orgs with the permissions the scraper needs (contents/issues/PRs read).","Record the token source and GitHub Request ID from the message when escalating."],"tags":["github","authentication","http-401","http-403","scopes"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}