{"record":{"id":"d42a1595de6145a0","repo":"can1357/oh-my-pi","slug":"unexpected-redirect-to-location-r-resource-may","errorCode":null,"errorMessage":"unexpected redirect to {location!r}; resource may have moved","messagePattern":"unexpected redirect to (.+?); resource may have moved","errorType":"http","errorClass":"GitHubError","httpStatus":null,"severity":"error","filePath":"python/robomp/src/github_client.py","lineNumber":273,"sourceCode":"            timeout=httpx.Timeout(30.0, connect=10.0),\n            follow_redirects=True,\n        )\n\n    # ---- request helpers ----\n    def _check(self, resp: httpx.Response) -> Any:\n        if resp.status_code >= 400:\n            retry_after = _parse_retry_after(resp)\n            try:\n                msg = resp.json().get(\"message\", resp.text)\n            except Exception:\n                msg = resp.text\n            raise GitHubError(resp.status_code, str(msg), retry_after=retry_after)\n        if resp.status_code >= 300:\n            # Redirect we couldn't (or weren't asked to) follow. GitHub uses 301\n            # for transferred repos / issues. Surface as a normal error so host\n            # tools map it to RpcCommandError instead of mis-parsing the body.\n            location = resp.headers.get(\"location\", \"\")\n            raise GitHubError(\n                resp.status_code,\n                f\"unexpected redirect to {location!r}; resource may have moved\",\n            )\n        if resp.status_code == 204 or not resp.content:\n            return None\n        return resp.json()\n\n    _TRANSIENT_RETRY_DELAYS = (1.0, 3.0, 10.0)\n    \"\"\"Backoff schedule for transient connection/timeout/5xx errors.\"\"\"\n\n    _TRANSIENT_STATUSES = frozenset({500, 502, 503, 504})\n    \"\"\"Upstream statuses treated as transient — retried for idempotent methods only.\"\"\"\n\n    _IDEMPOTENT_METHODS = frozenset({\"GET\", \"HEAD\"})\n    \"\"\"Methods safe to replay: a lost response cannot have caused a visible write.\"\"\"\n\n    def _transient_5xx(self, method: str, exc: GitHubError) -> bool:\n        return method.upper() in self._IDEMPOTENT_METHODS and exc.status in self._TRANSIENT_STATUSES","sourceCodeStart":255,"sourceCodeEnd":291,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/python/robomp/src/github_client.py#L255-L291","documentation":"GitHubError raised by _check when the API returns a 3xx redirect that could not (or was not configured to) be followed. GitHub uses 301 for transferred repos/issues; the library surfaces it as a normal error so host tools map it to RpcCommandError instead of mis-parsing the redirect body.","triggerScenarios":"Calling an endpoint for a repo or issue that has been transferred/renamed to another owner; following an old URL after repo ownership changed; redirects disabled in the client.","commonSituations":"Repo moved to a new organization, issue URL recorded before a repo transfer, bookmarks/cache pointing at pre-transfer slugs.","solutions":["Use the `location` header / new owner/repo path in your requests","Update stored references to the transferred repo","Re-check `github.com/owner/repo` in a browser to find the canonical new location"],"exampleFix":"// before: stale transferred repo slug\ndata = client.request(\"GET\", \"/repos/old-org/old-repo/issues/5\")\n// after: current slug after transfer\ndata = client.request(\"GET\", \"/repos/new-org/new-repo/issues/5\")","handlingStrategy":"try-catch","validationCode":"# verify the repo still exists at the expected slug\nresp = client.request(\"GET\", f\"/repos/{owner}/{repo}\")  # will raise GitHubError 301/404 if moved","typeGuard":null,"tryCatchPattern":"try:\n    data = client.request(\"GET\", path)\nexcept GitHubError as e:\n    if \"unexpected redirect\" in str(e):\n        raise StaleRepoRef(path) from e\n    raise","preventionTips":["Refresh stored repo slugs after org transfers","Follow the location header once to discover the new path and persist it","Subscribe to repo-transfer events if you mirror many repos"],"tags":["github","redirect","api"],"backgroundTag":"http-redirect-not-followed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}