odysseus-dev/odysseus · error · HTTPException

Invalid local_dir — path segments cannot start with '-'

Error message

Invalid local_dir — path segments cannot start with '-'

What it means

HTTP 400 from _validate_local_dir() in routes/cookbook_helpers.py. After the path passes the character allowlist, each segment (split on / and backslash) is checked for a leading '-'. A segment like -rf could be parsed as a CLI flag by hf/vllm/etc. when the path is embedded in a command — quoting does not prevent option parsing, so the guard lives in the validator itself (see the in-code comment).

Source

Thrown at routes/cookbook_helpers.py:128

    return token


def _validate_local_dir(v: str | None) -> str | None:
    if v is None or v == "":
        return None
    if len(v) >= 2 and v[0] == v[-1] and v[0] in {"'", '"'}:
        v = v[1:-1]
    v = v.rstrip("/") or "/"
    if not (_LOCAL_DIR_RE.match(v) or _WINDOWS_LOCAL_DIR_RE.match(v)):
        raise HTTPException(400, "Invalid local_dir — must be an absolute or ~ path with no shell metacharacters")
    # Reject path segments that start with '-' (option injection). '-' is in the
    # allowlist, so a dir like ``/models/-rf`` or ``D:\models\-rf`` could be read
    # as a CLI flag by hf/etc. — and quoting does NOT stop a value from being
    # parsed as an option. This is the one residual that command-build-time
    # quoting can't cover, so the guard lives here, keeping the safety wholly
    # inside the validator rather than relying on consumers.
    if any(seg.startswith("-") for seg in re.split(r"[\\/]", v) if seg):
        raise HTTPException(400, "Invalid local_dir — path segments cannot start with '-'")
    return v


def _validate_gpus(v: str | None) -> str | None:
    if v is None or v == "":
        return None
    if not _GPU_LIST_RE.fullmatch(str(v)):
        raise HTTPException(400, "Invalid gpus — expected comma-separated GPU indexes")
    return str(v)


def _shell_path(p: str) -> str:
    """Render a validated path for a double-quoted shell context, expanding a
    leading ~ to $HOME (single quotes wouldn't expand it). Safe because
    _validate_local_dir already rejects quotes and shell metacharacters."""
    if p == "~":
        return '"$HOME"'
    if p.startswith("~/"):

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Rename the directory so no segment starts with '-' (e.g. ~/models/checkpoint instead of ~/models/-checkpoint)
  2. If you genuinely need such a name, keep it outside local_dir and symlink to it from a safe name

Example fix

// before
{"local_dir": "/data/-snapshot"}
// after
{"local_dir": "/data/snapshot"}
Defensive patterns

Strategy: validation

Validate before calling

import re

def no_dash_segments(path: str) -> bool:
    return not any(seg.startswith("-") for seg in re.split(r"[\\/]", path) if seg)

if not no_dash_segments(local_dir):
    raise ValueError("rename directory: segments may not start with '-'")

Prevention

When it happens

Trigger: local_dir="/models/-rf", local_dir="D:\\models\\-opt", or any directory whose name begins with a hyphen such as "~/models/-checkpoint".

Common situations: User-created directories that happen to start with '-' (some tools generate '-checkpoint' style names); attempts at option injection through the path field.

Related errors


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