odysseus-dev/odysseus · error · SkillImportError

skills.sh did not redirect to GitHub — open the skill's repo

Error message

skills.sh did not redirect to GitHub — open the skill's repository on GitHub, navigate to the exact skill folder or SKILL.md file, and paste that URL; the repository-root link alone is not sufficient

What it means

Raised by parse_skill_source (services/memory/skill_importer.py) when a skills.sh URL was fetched but its final redirect target (r.url after the validated redirect chain) is not on a GitHub host. skills.sh is only usable as an indirection to GitHub: the importer follows its redirect, and if it does not end on GitHub it refuses — deliberately, because scraping the skills.sh page body cannot resolve the correct skill subdirectory (pages only link the repo root), which would silently import the wrong bundle.

Source

Thrown at services/memory/skill_importer.py:301

        url = "https://" + url

    parsed = urlparse(url)
    hostname = (parsed.hostname or "").lower()
    if hostname not in _GITHUB_HOSTS and hostname not in _SKILLS_SH_HOSTS:
        raise SkillImportError("Only GitHub or skills.sh URLs are supported")

    # A skills.sh link is only usable if it redirects to an exact supported
    # GitHub host. Scraping the page body for a github.com link cannot work:
    # skill pages only ever link the repository root, never the skill's
    # subdirectory, so the scrape resolves every skill in a repo to the same
    # (wrong) bundle. Fail with an actionable message instead.
    if hostname in _SKILLS_SH_HOSTS:
        r = _get_checked(url, timeout=20.0)
        if r.status_code >= 400:
            raise _github_response_error(r)
        final = str(r.url)
        if _github_host(final) not in _GITHUB_HOSTS:
            raise SkillImportError(
                "skills.sh did not redirect to GitHub — open the skill's "
                "repository on GitHub, navigate to the exact skill folder or "
                "SKILL.md file, and paste that URL; the repository-root link "
                "alone is not sufficient"
            )
        url = final

    # Update parsed and hostname to reflect the new GitHub URL
    parsed = urlparse(url)
    hostname = (parsed.hostname or "").lower()

    _assert_github_url(url)

    if hostname == "raw.githubusercontent.com":
        # /owner/repo/ref/path/to/file
        bits = [p for p in parsed.path.split("/") if p]
        if len(bits) < 4:
            raise SkillImportError("Invalid raw GitHub URL")

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Open the skill's page on skills.sh in a browser, click through to GitHub, navigate to the exact skill folder (or SKILL.md), and paste that github.com URL directly.
  2. Verify what the link redirects to: curl -sIL <skills.sh url> | grep -i '^location' — confirm the final hop is github.com.
  3. If the repo moved, find its new GitHub home and import from there.
  4. If you rely on a mirror domain, note it is unsupported by design; import the repo's canonical GitHub URL.

Example fix

# before
import_skill("https://skills.sh/owner/repo/skill")   # redirect lands on skills.sh CDN
SkillImportError: skills.sh did not redirect to GitHub — ...

# after
# browse to the skill on GitHub and paste the exact folder URL
import_skill("https://github.com/owner/repo/tree/main/skills/skill")
Defensive patterns

Strategy: fallback

Validate before calling

def resolve_skills_sh_url(url: str) -> str | None:
    with httpx.Client(follow_redirects=False, timeout=20) as c:
        r = c.get(url)
    final = str(r.url)
    return final if (urlparse(final).hostname or '').lower() in _GITHUB_HOSTS and r.status_code < 400 else None

Type guard

def skills_sh_redirects_to_github(url: str) -> bool:
    return resolve_skills_sh_url(url) is not None

Try / catch

from services.memory.skill_importer import SkillImportError

try:
    src = parse_skill_source(skills_sh_url)
except SkillImportError as e:
    if 'did not redirect to GitHub' in str(e):
        # fallback: user provides the exact GitHub folder URL manually
        src = parse_skill_source(prompt_user_for_github_url())
    else:
        raise

Prevention

When it happens

Trigger: skills.sh changes behavior and serves content directly (or redirects to its own CDN) instead of to github.com; a skills.sh URL for a skill whose repo was deleted or moved off GitHub; network middleware rewriting the redirect target; a skills.sh vanity link that redirects to a non-allowlisted mirror like gitmirror.com.

Common situations: skills.sh outage or redesign days; deleted/moved upstream repositories; mirrors and proxies (ghproxy, gitmirror) used in regions with poor GitHub access; stale skills.sh links shared in chat.

Related errors


AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14). Data as JSON: /api/errors/37bb9ee2d3d74a54. Report an issue: GitHub.