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
- 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.
- Verify what the link redirects to: curl -sIL <skills.sh url> | grep -i '^location' — confirm the final hop is github.com.
- If the repo moved, find its new GitHub home and import from there.
- 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
- Teach users to paste the github.com tree/blob URL of the exact skill folder, not the skills.sh link.
- When skills.sh behaves oddly, verify with curl -sIL where its redirects actually land.
- Do not scrape page bodies for GitHub links — the source comments explain why that resolves every skill to the wrong bundle.
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
- {context} must stay on GitHub (got {host or 'unknown host'})
- Invalid raw GitHub URL
- Invalid GitHub URL
- d && d.detail ? d.detail : ('HTTP ' + res.status)
- Server did not return a session id
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/37bb9ee2d3d74a54.
Report an issue: GitHub.