odysseus-dev/odysseus · error · SkillImportError
unsupported URL scheme: {scheme}
Error message
unsupported URL scheme: {scheme} What it means
Raised by parse_skill_source (services/memory/skill_importer.py) when the URL carries an explicit scheme:// form whose scheme is neither http nor https — e.g. file://, ftp://, ssh://, javascript:, or a non-web scheme. The guard distinguishes real schemes from urlparse artifacts (schemeless 'host:port' or opaque schemes), and only the true scheme:// shape raises this specific error; unsupported hosts in other shapes get the host-based message instead.
Source
Thrown at services/memory/skill_importer.py:278
continue
return r
raise SkillImportError("too many redirects while fetching skill bundle")
def parse_skill_source(url: str) -> ResolvedSource:
"""Normalize skills.sh / GitHub web URLs into owner/repo/ref/path."""
url = (url or "").strip()
if not url:
raise SkillImportError("URL is required")
# ``urlparse`` only reports an unambiguous scheme when the URL carries the
# ``scheme://`` form. Opaque schemes (``mailto:``, ``javascript:``) and a
# schemeless ``host:port`` both parse a "scheme" that is not one, so they
# fall through to the host check below and are rejected on the host instead.
scheme = urlparse(url).scheme.lower()
if scheme not in ("http", "https"):
if scheme and url.lower().startswith(f"{scheme}://"):
raise SkillImportError(f"unsupported URL scheme: {scheme}")
# Schemeless "github.com/owner/repo" — accept only a supported host.
rough_host = (urlparse("//" + url).hostname or "").lower()
if rough_host not in _GITHUB_HOSTS and rough_host not in _SKILLS_SH_HOSTS:
raise SkillImportError("Only GitHub or skills.sh URLs are supported")
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)View on GitHub (pinned to f9235ebbf1)
Solutions
- Use an https:// GitHub web URL: https://github.com/<owner>/<repo>/tree/<ref>/<skill-path>.
- Convert SSH remotes to HTTPS: git@github.com:owner/repo.git → https://github.com/owner/repo.
- For a local skill, do not use file:// — push it to a GitHub repo first or copy it into the skills directory manually if your tooling allows.
- Fix typo'd schemes (htps:// → https://).
Example fix
# before
import_skill("file:///Users/me/skills/foo")
SkillImportError: unsupported URL scheme: file
# after
import_skill("https://github.com/me/repo/tree/main/skills/foo") Defensive patterns
Strategy: validation
Validate before calling
from urllib.parse import urlparse
def assert_http_url(url: str) -> None:
scheme = urlparse(url.strip()).scheme.lower()
if scheme not in ('http', 'https', ''): # '' = schemeless host/path, handled by host check
raise ValueError(f'unsupported scheme {scheme!r}; use https://github.com/...') Type guard
def is_http_or_schemeless(url: str) -> bool:
return urlparse(url.strip()).scheme.lower() in ('http', 'https', '') Try / catch
try:
src = parse_skill_source(url)
except SkillImportError as e:
if 'unsupported URL scheme' in str(e):
# convert common SSH/remote forms to https
url = url.replace('git@github.com:', 'https://github.com/').removesuffix('.git')
src = parse_skill_source(url)
else:
raise Prevention
- Convert git remotes to HTTPS before feeding them to web-URL parsers (git remote get-url origin + string replace).
- Reject file:// and other schemes client-side with a hint about the accepted format.
- Document the accepted URL forms next to the import input field.
When it happens
Trigger: Importing a skill from file:///Users/me/skills/foo; git@github.com:owner/repo.git style SSH URLs misparsed as a scheme; ftp:// or http+insecure:// URLs; users pasting a javascript: or data: string; any pasted URL with a typo'd scheme like htps:// that urlparse still treats as a scheme when followed by //.
Common situations: Users pasting the SSH clone URL instead of the HTTPS web URL; trying to import a locally cloned skill via file://; CI passing a git-remote-style URL where a browser URL is expected.
Related errors
- {context} must stay on GitHub (got {host or 'unknown host'})
- Only GitHub or skills.sh URLs are supported
- Invalid GitHub URL
- unsafe path: {rel!r}
- outbound URL blocked: {reason}
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/8c4f78ef8c259b05.
Report an issue: GitHub.