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.endpoint

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Re-launch via POST /cookbook/serve with the actual serving command, which saves a real preset.
  2. If you know the real command, edit the preset's cmd field in cookbook_state.json so it no longer starts with '(adopted'.
  3. 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

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


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