odysseus-dev/odysseus · error · HTTPException

Invalid characters in cmd

Error message

Invalid characters in cmd

What it means

HTTP 400 from the serve-cmd validator in routes/cookbook_helpers.py (~line 764). After collapsing backslash-newline continuations into spaces, the command is scanned for characters that are never legitimate in this field: backticks and raw CR/LF newlines. Their presence suggests command substitution or multi-line shell input that the leading-token allowlist cannot reason about safely.

Source

Thrown at routes/cookbook_helpers.py:764

    `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 the
    pre-fix world) could pass arbitrary shell payloads.

    Leading env-var assignments (e.g. `CUDA_VISIBLE_DEVICES=0 python3 ...`)
    are stripped before checking the binary — several of our cmd builders
    prepend them, and they shouldn't trip the allowlist.
    """
    if v is None or v == "":
        return None
    # Collapse backslash-newline line continuations into single spaces. Serve
    # commands (vLLM especially) are routinely pasted multi-line with trailing
    # `\` — that's a safe shell/shlex continuation, so the command stays ONE
    # logical invocation and the leading-token allowlist below still governs.
    v = re.sub(r"\\[ \t]*\r?\n[ \t]*", " ", v).strip()
    # Backticks and raw newlines are never legitimate here.
    if any(c in v for c in ("`", "\n", "\r")):
        raise HTTPException(400, "Invalid characters in cmd")

    # Known GGUF launcher prelude → validate the serve invocation(s) it guards.
    m = _GGUF_PRELUDE_RE.match(v)
    if m:
        rest = v[m.end():]
        # rest is `[ENV=…] python3 -m llama_cpp.server … || [ENV=…] llama-server …`
        for part in rest.split("||"):
            _check_serve_binary(part.strip())
        return v

    # Otherwise: a single invocation — no shell metacharacters allowed. Replace
    # only the exact command substitutions emitted by the Cookbook UI:
    # $(printf %s 'safe-path') and the mmproj lookup
    # $(find <path> -iname 'mmproj*.gguf' 2>/dev/null | sort | head -1).
    def _replace_safe_subshell(match: re.Match[str]) -> str:
        subshell = match.group(0)
        return "/placeholder/safe/path" if _is_safe_serve_subshell(subshell) else subshell

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Replace backtick substitutions with literal values before submitting
  2. Keep the command on one logical line; use trailing backslash only for line continuations
  3. Strip CR characters client-side when submitting from Windows (normalize CRLF to spaces)
  4. Compute any dynamic value client-side and inline the result into cmd

Example fix

# before
cmd = "vllm serve `cat /tmp/model.txt` --port 8000"
# after
cmd = "vllm serve mistralai/Mistral-7B --port 8000"
Defensive patterns

Strategy: validation

Validate before calling

import re

def clean_serve_cmd(cmd: str) -> str:
    cmd = re.sub(r"\\[ \t]*\r?\n[ \t]*", " ", cmd).strip()
    if any(c in cmd for c in ("`", "\n", "\r")):
        raise ValueError("cmd contains backtick or raw newline")
    return cmd

Prevention

When it happens

Trigger: cmd containing a backtick substitution such as "vllm serve `cat /tmp/model.txt` --port 8000"; a cmd pasted with embedded newlines that are not backslash continuations; a CRLF paste leaving a raw carriage return.

Common situations: Windows users pasting CRLF commands so a stray CR survives; users writing backtick-style substitution instead of $(); copying a multi-line shell heredoc into the cmd field.

Understand the failure class

Related errors


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