unslothai/unsloth · error · HTTPException

Invalid repo_id: {repo_id!r}

Error message

Invalid repo_id: {repo_id!r}

What it means

HTTP 400 from the start-dataset-download endpoint when the submitted repo_id fails the `_is_valid_repo_id` check after stripping whitespace. The validator enforces the HuggingFace `namespace/name` shape (or bare name) and rejects empty strings, bad characters, or malformed paths before any network work happens.

Source

Thrown at studio/backend/hub/services/datasets/downloads.py:159

        variant = None,
    )
    return DatasetDownloadJobStatus(state = state, error = error, generation = generation)


async def download_dataset_response(
    body: DownloadDatasetRequest,
    hf_token: Optional[str] = None,
    *,
    allow_ambient_token: bool = True,
) -> dict:
    """Start a background download for a HuggingFace dataset.

    ``allow_ambient_token=False`` keeps the worker anonymous when the caller sent no token, for
    repos named over the API rather than chosen here.
    """
    repo_id = body.repo_id.strip()
    if not _is_valid_repo_id(repo_id):
        raise HTTPException(
            status_code = 400,
            detail = f"Invalid repo_id: {repo_id!r}",
        )
    # Canonicalize so two different-cased paste-ins share one job + cache dir.
    repo_id = await asyncio.to_thread(resolve_cached_repo_id_case, repo_id, repo_type = "dataset")
    key = _download_job_key(repo_id)

    # Off the event loop: resolving "auto" can run the Xet reachability probe, and a blackholed DNS
    # makes that outlast its 3s budget while every other Studio request waits behind it.
    use_xet, transport_reason = await asyncio.to_thread(
        download_lifecycle.resolve_requested_use_xet,
        getattr(body, "transport_mode", None),
        body.use_xet,
    )
    transport = download_lifecycle.resolve_transport(use_xet)
    logger.info("Download transport for %s: %s (%s)", repo_id, transport, transport_reason)
    from utils.hf_cache_settings import get_hf_cache_paths

View on GitHub (pinned to 203007d190)

Solutions

  1. Send the canonical repo id (`owner/dataset` or `dataset`), not a full huggingface.co URL.
  2. Trim the input client-side and reject empty values before submitting.
  3. Mirror the same repo-id grammar client-side (namespace/name, [A-Za-z0-9_.-]) to fail fast.
  4. If the id looks right, log the repr — the f-string uses {repo_id!r}, so hidden characters will show up quoted.

Example fix

# before
body = DownloadDatasetRequest(repo_id="https://huggingface.co/datasets/squad")
# after
body = DownloadDatasetRequest(repo_id="squad")
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(repo_id: str) -> bool:
    repo_id = repo_id.strip()
    return bool(repo_id) and len(repo_id) <= 96 and bool(REPO_ID_RE.match(repo_id))

Type guard

def is_download_request_valid(body) -> TypeGuard[DownloadDatasetRequest]:
    return isinstance(body.repo_id, str) and is_valid_repo_id(body.repo_id)

Try / catch

try:
    start_download(client, body)
except HTTPStatusError as e:
    if e.response.status_code == 400:
        show_field_error("repo_id", e.response.json()["detail"])  # includes !r repr: reveals hidden chars
    else:
        raise

Prevention

When it happens

Trigger: POSTing a download request with an empty repo_id, leading/trailing-only whitespace, embedded '..' or slashes in the wrong places, or non-ASCII/control characters that do not match the repo-id grammar.

Common situations: Form submitted before the user finished typing; pasted URL (`https://huggingface.co/datasets/foo/bar`) instead of the repo id; copy-paste introduced a trailing newline or invisible character.

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/498fbaddc9cc3072. Report an issue: GitHub.