odysseus-dev/odysseus · error · HTTPException

Invalid repo_id — must be <org>/<name> using [A-Za-z0-9._-]

Error message

Invalid repo_id — must be <org>/<name> using [A-Za-z0-9._-]

What it means

HTTP 400 from _validate_repo_id() in routes/cookbook_helpers.py. The value must fully match _REPO_ID_RE, i.e. a Hugging Face style <org>/<name> built only from [A-Za-z0-9._-]. Empty, None, extra slashes, spaces, unicode, or a missing org/name split all fail.

Source

Thrown at routes/cookbook_helpers.py:67

# shell-safe: none of ``; & | ` $ '' "" () {}`` newlines etc. are in ``[\w. -]``,
# so injection vectors remain rejected. A leading ~ is expanded to $HOME at
# command-build time. (Drive letters stay ASCII: ``[A-Za-z]:``.)
_LOCAL_DIR_RE = re.compile(r"^~?(?:/[\w. -]*)+$|^~$")
_WINDOWS_LOCAL_DIR_RE = re.compile(r"^[A-Za-z]:[\\/](?:[\w. -]+(?:[\\/][\w. -]+)*[\\/]?)?$")
_WINDOWS_DRIVE_PATH_RE = re.compile(r"^[A-Za-z]:[\\/]")


def _git_bash_path(path: str) -> str:
    m = re.match(r"^([A-Za-z]):[\\/](.*)$", path)
    if not m:
        return path
    drive, rest = m.groups()
    return f"/{drive.lower()}/{rest.replace(chr(92), '/')}"


def _validate_repo_id(v: str | None) -> str:
    if not v or not _REPO_ID_RE.match(v):
        raise HTTPException(400, "Invalid repo_id — must be <org>/<name> using [A-Za-z0-9._-]")
    return v


def _validate_serve_model_id(v: str | None) -> str:
    if not v:
        raise HTTPException(400, "repo_id is required")
    if _REPO_ID_RE.match(v) or _LOCAL_MODEL_ID_RE.match(v) or _OLLAMA_MODEL_ID_RE.match(v):
        return v
    raise HTTPException(400, "Invalid repo_id — must be <org>/<name>, an Ollama name:tag, or a cached local model id")


def _validate_include(v: str | None) -> str | None:
    if v is None or v == "":
        return None
    if not _INCLUDE_RE.match(v):
        raise HTTPException(400, "Invalid include pattern")
    return v

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Send the exact Hugging Face repo id: <org>/<name>, e.g. mistralai/Mistral-7B-Instruct-v0.2
  2. Strip whitespace and drop the https://huggingface.co/ prefix before sending
  3. If you meant an Ollama or local cached model, use an endpoint whose validator accepts those (_validate_serve_model_id)
  4. Check _REPO_ID_RE at the top of routes/cookbook_helpers.py for the authoritative pattern

Example fix

// before
{"repo_id": "https://huggingface.co/mistralai/Mistral-7B-Instruct-v0.2"}
// after
{"repo_id": "mistralai/Mistral-7B-Instruct-v0.2"}
Defensive patterns

Strategy: validation

Validate before calling

import re
REPO_ID_RE = re.compile(r"^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$")

def is_valid_repo_id(v):
    return bool(v) and bool(REPO_ID_RE.match(v))

assert is_valid_repo_id("mistralai/Mistral-7B-Instruct-v0.2")

Try / catch

try:
    r = client.post("/cookbook/download", json={"repo_id": rid})
except HTTPException as e:
    if e.status_code == 400 and "repo_id" in e.detail:
        rid = prompt_user_for_exact_hf_id()
    else:
        raise

Prevention

When it happens

Trigger: Calling a cookbook endpoint that validates repo_id with values like "model" (no slash), "org/a/b" (two slashes), "org/name extra" (space), "" or null, or an Ollama id ("llama3:8b") sent to an endpoint that only accepts HF repo ids.

Common situations: Passing a bare model name without the org; copying a full HF URL (https://huggingface.co/org/name) instead of the id; trailing whitespace or invisible unicode from copy-paste.

Related errors


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