odysseus-dev/odysseus · error · HTTPException
cmd binary '{base or '(empty)'}' is not allowed. Must start
Error message
cmd binary '{base or '(empty)'}' is not allowed. Must start with one of: {', '.join(sorted(_SERVE_CMD_ALLOWLIST))} What it means
HTTP 400 from _check_serve_binary() in routes/cookbook_helpers.py: the command's first meaningful token (after skipping leading VAR=value assignments like CUDA_VISIBLE_DEVICES=0) has a basename that is not in _SERVE_CMD_ALLOWLIST. Only allowlisted server binaries may be launched; everything else — shells, curl, arbitrary executables — is refused.
Source
Thrown at routes/cookbook_helpers.py:730
return f"{match.group('flag')}{match.group('sep')}{quote}{mapped}{quote}"
return _LLAMA_CPP_PYTHON_TYPE_FLAG_RE.sub(repl, cmd)
def _check_serve_binary(seg: str) -> None:
"""Validate that a single command segment starts with an allowlisted binary
(after skipping leading env-var assignments like `CUDA_VISIBLE_DEVICES=0`)."""
try:
tokens = shlex.split(seg) if seg.strip() else []
except ValueError:
raise HTTPException(400, "Invalid cmd — could not parse")
if not tokens:
return
env_re = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
first = next((t for t in tokens if not env_re.match(t)), "")
base = os.path.basename(first)
if base not in _SERVE_CMD_ALLOWLIST:
raise HTTPException(
400,
f"cmd binary '{base or '(empty)'}' is not allowed. Must start with one of: "
f"{', '.join(sorted(_SERVE_CMD_ALLOWLIST))}",
)
def _is_safe_serve_subshell(subshell: str) -> bool:
return bool(
_SAFE_PRINTF_SUBSHELL_RE.fullmatch(subshell)
or _SAFE_FIND_MMPROJ_SUBSHELL_RE.fullmatch(subshell)
)
def _validate_serve_cmd(v: str | None) -> str | None:
"""Reject serve commands that aren't in the allowlist or contain shell metachars.
`req.cmd` is dropped verbatim into a bash/PowerShell wrapper script and
executed in a tmux session. Without this gate, an admin (or anyone in theView on GitHub (pinned to f9235ebbf1)
Solutions
- Start the command directly with an allowlisted binary — the error message lists the full sorted allowlist (e.g. vllm, llama-server, python3)
- Drop bash -c / sh -c / ./script.sh wrappers and invoke the server binary itself
- If a new binary is legitimately needed, add it to _SERVE_CMD_ALLOWLIST in routes/cookbook_helpers.py and review the security implications
- Put environment setup in env_prefix or leading VAR=value assignments, not in a wrapper command
Example fix
# before cmd = "bash -c 'vllm serve mistralai/Mistral-7B --port 8000'" # after cmd = "vllm serve mistralai/Mistral-7B --port 8000"
Defensive patterns
Strategy: validation
Validate before calling
import os, re, shlex
# Mirror the server list; keep in sync with _SERVE_CMD_ALLOWLIST in cookbook_helpers.py:
_SERVE_CMD_ALLOWLIST = {"vllm", "llama-server", "python3"}
def first_binary(cmd: str) -> str:
tokens = shlex.split(cmd)
env_re = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*=")
first = next((t for t in tokens if not env_re.match(t)), "")
return os.path.basename(first)
if first_binary(cmd) not in _SERVE_CMD_ALLOWLIST:
raise ValueError(f"binary {first_binary(cmd)!r} not allowlisted") Prevention
- Invoke the server binary directly; no bash -c, sh, or wrapper scripts
- Prefer python3 over python
- Sync your client allowlist with _SERVE_CMD_ALLOWLIST whenever the backend updates
When it happens
Trigger: cmd="bash -c 'vllm serve ...'", cmd="./start.sh", cmd="python -m myserver ..." (python vs python3), or a segment containing only env assignments, yielding '(empty)'.
Common situations: Using a wrapper script or shell to start the model server; using 'python' instead of 'python3'; a new or renamed inference binary that has not been added to _SERVE_CMD_ALLOWLIST yet; trying to run arbitrary commands through the serve endpoint.
Related errors
- Invalid pip package name
- repo_id is required
- Invalid repo_id — must be <org>/<name>, an Ollama name:tag,
- Invalid local_dir — path segments cannot start with '-'
- Invalid cmd — could not parse
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/007af60c5b089681.
Report an issue: GitHub.