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_STATUSES

View on GitHub (pinned to 9690622007)

Solutions

  1. Use the `location` header / new owner/repo path in your requests
  2. Update stored references to the transferred repo
  3. 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

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


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/d42a1595de6145a0. Report an issue: GitHub.