{"record":{"id":"e4b03b9465d93768","repo":"odysseus-dev/odysseus","slug":"invalid-pip-package-name","errorCode":null,"errorMessage":"Invalid pip package name","messagePattern":"Invalid pip package name","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"routes/cookbook_routes.py","lineNumber":2028,"sourceCode":"            # The previous regex turned that URL into\n            #   https://abetlen.github.io/llama-cpp-python[server]/whl/cu124\n            # which pip then couldn't resolve → silent fallback to source\n            # build of the .tar.gz → CPU-only binary (because CMAKE_ARGS\n            # isn't set), defeating the entire purpose of the CUDA index.\n            req.cmd = re.sub(r\"(?<![A-Za-z0-9_.\\-/])llama_cpp(?![A-Za-z0-9_.\\-/])\", \"llama-cpp-python[server]\", req.cmd)\n            req.cmd = re.sub(r\"(?<![A-Za-z0-9_.\\-/])llama-cpp-python(?![\\[/])\", \"llama-cpp-python[server]\", req.cmd)\n            if \"llama-cpp-python\" in req.cmd and \"--extra-index-url\" not in req.cmd:\n                req.cmd += \" --extra-index-url https://abetlen.github.io/llama-cpp-python/whl/cpu\"\n            # PEP-508-style package spec — letters, digits, `.-_` for the\n            # name; `[` `]` for extras; `<>=!~,` for version specifiers.\n            # v2 review HIGH-14: tightened from the previous regex which\n            # also allowed spaces and `+`, both of which can be abused to\n            # introduce extra shell tokens once interpolated into the\n            # serve command. We now use `re.fullmatch` and drop space/`+`.\n            if not req.repo_id or not re.fullmatch(\n                r\"[A-Za-z0-9][A-Za-z0-9._\\-\\[\\]<>=!,~]{0,200}\", req.repo_id\n            ):\n                raise HTTPException(400, \"Invalid pip package name\")\n        else:\n            _validate_serve_model_id(req.repo_id)\n        TMUX_LOG_DIR.mkdir(parents=True, exist_ok=True)\n        session_id = f\"serve-{uuid.uuid4().hex[:8]}\"\n        remote = req.remote_host\n        is_windows = req.platform == \"windows\"\n\n        # Ollama: if the user didn't pin a port, resolve the actual port we'll\n        # bind to here (before runner construction) by probing the target host.\n        # Otherwise the runner script picks one at runtime and `_auto_register`\n        # below still registers the stale 11434 default — which on a host with\n        # a systemd ollama lands on the wrong (unreachable-from-docker) service.\n        # Match \"ollama serve\" as a phrase (with optional flags after), not\n        # any substring containing \"ollama\" — otherwise commands like\n        # `docker exec ollama-test ollama-import …` get wrapped as if they\n        # were native `ollama serve`, prepending OLLAMA_HOST=… and then\n        # running the ollama-not-found preflight which exits 127.\n        if re.search(r\"\\bollama\\s+serve\\b\", req.cmd) and \"OLLAMA_HOST=\" not in req.cmd:","sourceCodeStart":2010,"sourceCodeEnd":2046,"githubUrl":"https://github.com/odysseus-dev/odysseus/blob/f9235ebbf13f693a6fd29ce70b097f6ec83705bf/routes/cookbook_routes.py#L2010-L2046","documentation":"HTTP 400 from the pip-install branch of the serve route in routes/cookbook_routes.py (~line 2028). Here req.repo_id doubles as a pip package spec and must fullmatch [A-Za-z0-9][A-Za-z0-9._\\-\\[\\]<>=!,~]{0,200} — a PEP-508-ish name with optional extras and version specifiers. The regex was tightened (v2 review HIGH-14) to use fullmatch and drop spaces and +, which could smuggle extra shell tokens into the serve command.","triggerScenarios":"repo_id=\"llama-cpp-python[server]>=0.2.90\" passes; \"llama-cpp-python server\" (space), \"torch==2.3.1+cu121\" (+ local tag), \"-e ./pkg\" (leading dash), or \"pkg@ git+https://...\" (@, :, /) all fail.","commonSituations":"Passing a pip URL/VCS spec (git+https) where only name[extras]+specifier is allowed; copying a whole `pip install` command line into the field; CUDA wheel versions with +local segments that must be dropped or matched exactly.","solutions":["Send a plain package name with optional extras and version specifier: llama-cpp-python[server]>=0.2.83","Remove spaces, + local version tags, @, :, and VCS URLs from the spec","Note the route auto-appends --extra-index-url for llama-cpp-python CPU wheels — do not add it via the name field","Pick the exact version from the wheel index so no +local segment is needed"],"exampleFix":"// before\n{\"repo_id\": \"torch==2.3.1+cu121\"}\n// after\n{\"repo_id\": \"torch==2.3.1\"}","handlingStrategy":"validation","validationCode":"import re\nPIP_SPEC = re.compile(r\"^[A-Za-z0-9][A-Za-z0-9._\\-\\[\\]<>=!,~]{0,200}$\")\n\ndef prevalidate_pip_spec(v):\n    v = (v or \"\").strip()\n    if not PIP_SPEC.fullmatch(v):\n        raise ValueError(\"pip spec must be name[extras]<spec>; no spaces, +, @, :, /\")\n    return v\n\nprevalidate_pip_spec(\"llama-cpp-python[server]>=0.2.83\")  # ok","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Send only name/extras/version — never full pip CLI strings or VCS URLs","Drop +local version segments (cu121 wheels) from specs","Let the route add --extra-index-url for llama-cpp-python itself"],"tags":["validation","pip","pep508","serve","http-400","security"],"backgroundTag":null,"analyzedSha":"f9235ebbf13f693a6fd29ce70b097f6ec83705bf","analyzedAt":"2026-08-14T21:47:48.359Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}