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
- Rename the directory so no segment starts with '-' (e.g. ~/models/checkpoint instead of ~/models/-checkpoint)
- 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
- Name model directories without a leading hyphen
- Treat '-'-prefixed dirs as an option-injection red flag
- Re-run the full _validate_local_dir mirror (see error 366) client-side
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
- Invalid local_dir — must be an absolute or ~ path with no sh
- Invalid pip package name
- await res.text()
- Invalid SSH remote host
- {key} must be an integer
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/ef612cb4583c14c9.
Report an issue: GitHub.