odysseus-dev/odysseus · warning · HTTPException
Preset {chosen.get('name')!r} has no launchable cmd (adopted
Error message
Preset {chosen.get('name')!r} has no launchable cmd (adopted from external launch). Use POST /cookbook/serve with the actual cmd instead. What it means
Raised when the matched preset exists but has no usable launch command: repo_id/cmd is empty, or the cmd starts with '(adopted', marking it as a session adopted from an external ssh launch. Adopted presets store no real command, so relaunching them is impossible and the API points the caller to POST /cookbook/serve.
Source
Thrown at routes/codex_routes.py:801
state = _read_cookbook_state()
presets = state.get("presets") or []
lname = name.lower().strip()
chosen = next(
(p for p in presets if isinstance(p, dict) and (p.get("name") or "").lower() == lname),
None,
)
if chosen is None:
chosen = next(
(p for p in presets if isinstance(p, dict) and lname in (p.get("name") or "").lower()),
None,
)
if chosen is None:
raise HTTPException(404, f"No preset matching {name!r}")
repo_id = chosen.get("model") or chosen.get("modelId") or ""
cmd = (chosen.get("cmd") or "").strip()
host = chosen.get("host") or chosen.get("remoteHost") or ""
if not repo_id or not cmd or cmd.startswith("(adopted"):
raise HTTPException(400, f"Preset {chosen.get('name')!r} has no launchable cmd "
"(adopted from external launch). Use POST /cookbook/serve "
"with the actual cmd instead.")
# Reuse the serve handler we already validated.
from routes.cookbook_helpers import ServeRequest
body = {"repo_id": repo_id, "cmd": cmd}
if host:
body["remote_host"] = host
try:
req = ServeRequest(**body)
except Exception as exc:
raise HTTPException(400, f"Preset payload invalid: {exc}")
serve_endpoint = _find_endpoint(None, "POST", "/api/model/serve")
if serve_endpoint is None:
from fastapi import FastAPI
app: FastAPI = request.app
for route in app.routes:
if getattr(route, "path", None) == "/api/model/serve" and "POST" in getattr(route, "methods", set()):
serve_endpoint = route.endpointView on GitHub (pinned to f9235ebbf1)
Solutions
- Re-launch via POST /cookbook/serve with the actual serving command, which saves a real preset.
- If you know the real command, edit the preset's cmd field in cookbook_state.json so it no longer starts with '(adopted'.
- Re-run the model manually and re-adopt only for tracking; use /cookbook/serve for relaunchable presets.
Example fix
// before
await post(`/cookbook/preset/${name}`) // preset cmd == '(adopted ...)'
// after
await post('/cookbook/serve', {repo_id, cmd: 'python -m vllm.entrypoints... ', remote_host}) Defensive patterns
Strategy: validation
Validate before calling
p = find_preset(name)
launchable = p and p.get('cmd') and not p['cmd'].startswith('(adopted') and p.get('model')
if not launchable: use POST /cookbook/serve instead Type guard
const isLaunchable = (p?: {cmd?:string; model?:string}) =>
!!p?.model && !!p?.cmd?.trim() && !p.cmd.trim().startsWith('(adopted') Prevention
- Treat adopted presets as track-only; never offer a 'relaunch' button for them.
- Save launchable presets via /cookbook/serve so a real cmd is stored.
When it happens
Trigger: POST /cookbook/preset/{name} where the chosen preset was created by POST /cookbook/adopt (cmd begins with '(adopted'), or the stored preset has model/cmd fields missing/blank.
Common situations: User adopted a manually-started tmux session, then tries to 'relaunch' it later after it died; preset JSON was hand-edited and cmd removed.
Related errors
- Preset payload invalid: {exc}
- await res.text()
- HTTP ${res.status}
- data.error || ('HTTP ' + (data.status || 500))
- model returned no rewritten text
AI-assisted analysis of odysseus-dev/odysseus@f9235ebbf1 (2026-08-14).
Data as JSON: /api/errors/41409b51be6c433b.
Report an issue: GitHub.