{"record":{"id":"d7083b41696d90ff","repo":"odysseus-dev/odysseus","slug":"invalid-cmd-could-not-parse","errorCode":null,"errorMessage":"Invalid cmd — could not parse","messagePattern":"Invalid cmd — could not parse","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/cookbook_helpers.py","lineNumber":723,"sourceCode":"\n    def repl(match: re.Match[str]) -> str:\n        value = match.group(\"value\")\n        mapped = _LLAMA_CPP_PYTHON_GGML_TYPES.get(value.lower())\n        if not mapped:\n            return match.group(0)\n        quote = match.group(\"quote\")\n        return f\"{match.group('flag')}{match.group('sep')}{quote}{mapped}{quote}\"\n\n    return _LLAMA_CPP_PYTHON_TYPE_FLAG_RE.sub(repl, cmd)\n\n\ndef _check_serve_binary(seg: str) -> None:\n    \"\"\"Validate that a single command segment starts with an allowlisted binary\n    (after skipping leading env-var assignments like `CUDA_VISIBLE_DEVICES=0`).\"\"\"\n    try:\n        tokens = shlex.split(seg) if seg.strip() else []\n    except ValueError:\n        raise HTTPException(400, \"Invalid cmd — could not parse\")\n    if not tokens:\n        return\n    env_re = re.compile(r\"^[A-Za-z_][A-Za-z0-9_]*=\")\n    first = next((t for t in tokens if not env_re.match(t)), \"\")\n    base = os.path.basename(first)\n    if base not in _SERVE_CMD_ALLOWLIST:\n        raise HTTPException(\n            400,\n            f\"cmd binary '{base or '(empty)'}' is not allowed. Must start with one of: \"\n            f\"{', '.join(sorted(_SERVE_CMD_ALLOWLIST))}\",\n        )\n\n\ndef _is_safe_serve_subshell(subshell: str) -> bool:\n    return bool(\n        _SAFE_PRINTF_SUBSHELL_RE.fullmatch(subshell)\n        or _SAFE_FIND_MMPROJ_SUBSHELL_RE.fullmatch(subshell)\n    )","sourceCodeStart":705,"sourceCodeEnd":741,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/cookbook_helpers.py#L705-L741","documentation":"HTTP 400 from _check_serve_binary() in routes/cookbook_helpers.py when shlex.split() raises ValueError while tokenizing a command segment — the cmd string has unbalanced quotes or an unterminated escape. The validator must tokenize the command to find its first real token; malformed quoting makes that impossible.","triggerScenarios":"cmd=\"vllm serve 'mistralai/Mistral-7B --port 8000\" (missing closing quote), a cmd ending in a lone backslash, or a stray unmatched quote inside a token.","commonSituations":"Hand-editing a serve command and deleting a closing quote; multi-line vLLM commands pasted with a broken line-continuation; JSON escaping bugs that turn an escaped quote into a dangling one.","solutions":["Balance all quotes in the cmd string — every opening ' or \\\" needs a close","Remove trailing lone backslashes; use backslash-newline continuations only at end of line","Validate with python -c \"import shlex; shlex.split(cmd)\" before submitting","If the command came from the UI builder, regenerate it rather than hand-patching"],"exampleFix":"# before\ncmd = \"vllm serve 'mistralai/Mistral-7B-Instruct-v0.2 --port 8000\"\n# after\ncmd = \"vllm serve mistralai/Mistral-7B-Instruct-v0.2 --port 8000\"","handlingStrategy":"validation","validationCode":"import shlex\n\ndef cmd_parses(cmd: str) -> bool:\n    try:\n        shlex.split(cmd)\n        return True\n    except ValueError:\n        return False\n\nassert cmd_parses(cmd), \"unbalanced quotes in cmd\"","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Run shlex.split on the command client-side before submitting","Keep commands on one logical line using backslash-newline continuations","Regenerate commands from the UI builder instead of hand-editing quotes"],"tags":["validation","shell-quoting","serve","http-400"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}