can1357/oh-my-pi · error · GitHubError
unexpected redirect to {location!r}; resource may have moved
Error message
unexpected redirect to {location!r}; resource may have moved What it means
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.
Source
Thrown at python/robomp/src/github_client.py:273
timeout=httpx.Timeout(30.0, connect=10.0),
follow_redirects=True,
)
# ---- request helpers ----
def _check(self, resp: httpx.Response) -> Any:
if resp.status_code >= 400:
retry_after = _parse_retry_after(resp)
try:
msg = resp.json().get("message", resp.text)
except Exception:
msg = resp.text
raise GitHubError(resp.status_code, str(msg), retry_after=retry_after)
if resp.status_code >= 300:
# Redirect we couldn't (or weren't asked to) follow. GitHub uses 301
# for transferred repos / issues. Surface as a normal error so host
# tools map it to RpcCommandError instead of mis-parsing the body.
location = resp.headers.get("location", "")
raise GitHubError(
resp.status_code,
f"unexpected redirect to {location!r}; resource may have moved",
)
if resp.status_code == 204 or not resp.content:
return None
return resp.json()
_TRANSIENT_RETRY_DELAYS = (1.0, 3.0, 10.0)
"""Backoff schedule for transient connection/timeout/5xx errors."""
_TRANSIENT_STATUSES = frozenset({500, 502, 503, 504})
"""Upstream statuses treated as transient — retried for idempotent methods only."""
_IDEMPOTENT_METHODS = frozenset({"GET", "HEAD"})
"""Methods safe to replay: a lost response cannot have caused a visible write."""
def _transient_5xx(self, method: str, exc: GitHubError) -> bool:
return method.upper() in self._IDEMPOTENT_METHODS and exc.status in self._TRANSIENT_STATUSESView on GitHub (pinned to 9690622007)
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
Example fix
// before: stale transferred repo slug
data = client.request("GET", "/repos/old-org/old-repo/issues/5")
// after: current slug after transfer
data = client.request("GET", "/repos/new-org/new-repo/issues/5") Defensive patterns
Strategy: try-catch
Validate before calling
# verify the repo still exists at the expected slug
resp = client.request("GET", f"/repos/{owner}/{repo}") # will raise GitHubError 301/404 if moved Try / catch
try:
data = client.request("GET", path)
except GitHubError as e:
if "unexpected redirect" in str(e):
raise StaleRepoRef(path) from e
raise Prevention
- 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
When it happens
Trigger: 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.
Common situations: Repo moved to a new organization, issue URL recorded before a repo transfer, bookmarks/cache pointing at pre-transfer slugs.
Related errors
- Invalid GitHub release metadata
- GitHubError(resp.status_code, str(msg), retry_after=retry_af
- An unknown error occurred
- Devin AssignModel error ${response.status} ${response.status
- GitLab Duo Workflow create failed with HTTP ${response.statu
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d42a1595de6145a0.
Report an issue: GitHub.