odysseus-dev/odysseus · error · HTTPException

Invalid env_prefix

Error message

Invalid env_prefix

What it means

HTTP 400 from the env_prefix normalizer in routes/cookbook_helpers.py (~line 1167) when shlex.split(ep, posix=True) raises ValueError — the env_prefix has unbalanced quotes or an unterminated escape, so it cannot be tokenized into a source-command form.

Source

Thrown at routes/cookbook_helpers.py:1167

    """Build SSH command string with optional port."""
    pf = f"-p {port} " if port and port != "22" else ""
    return f"ssh {pf}{host} '{cmd}'"


def _safe_env_prefix(ep: str | None) -> str | None:
    """Rewrite a `source <path>` env_prefix so it no-ops if the path is missing.
    Prevents `line N: <path>: No such file or directory` errors when a serve
    task is launched against a host that doesn't have the expected venv.

    Also rewrites leading `~/` → `$HOME/` so the path expands inside double
    quotes (bash only tilde-expands unquoted tokens at word start)."""
    if not ep:
        return ep
    import shlex
    try:
        parts = shlex.split(ep, posix=True)
    except ValueError:
        raise HTTPException(400, "Invalid env_prefix")
    if len(parts) != 2 or parts[0] not in {"source", "."}:
        # Bash conda activation emitted by the frontend:
        #   eval "$(conda shell.bash hook)" && conda activate ENV
        m = re.fullmatch(r'eval "\$\(conda shell\.bash hook\)" && conda activate (.+)', ep)
        if m:
            env = m.group(1).strip()
            try:
                env_parts = shlex.split(env, posix=True)
            except ValueError:
                raise HTTPException(400, "Invalid env_prefix")
            if len(env_parts) != 1:
                raise HTTPException(400, "Invalid env_prefix")
            return 'eval "$(conda shell.bash hook)" && conda activate ' + shlex.quote(env_parts[0])

        # Plain conda activation, used by Windows/PowerShell and some manual callers.
        if len(parts) == 3 and parts[0] == "conda" and parts[1] == "activate":
            return "conda activate " + shlex.quote(parts[2])

View on GitHub (pinned to f9235ebbf1)

Solutions

  1. Balance the quotes: source \"/opt/my venv/bin/activate\"
  2. Avoid backslashes except as proper escapes; for Windows paths prefer forward slashes
  3. Generate the string with shlex.quote() so quoting is always well-formed
  4. Test with python -c 'import shlex; shlex.split(your_string)' before submitting

Example fix

# before
env_prefix = "source '/opt/my venv/bin/activate"
# after
env_prefix = 'source "/opt/my venv/bin/activate"'
Defensive patterns

Strategy: validation

Validate before calling

import shlex

def env_prefix_parses(ep: str) -> bool:
    try:
        shlex.split(ep, posix=True)
        return True
    except ValueError:
        return False

assert env_prefix_parses(env_prefix), "unbalanced quotes in env_prefix"

Prevention

When it happens

Trigger: env_prefix="source '/opt/venv with space/bin/activate" (opening quote never closed), or a prefix ending in a bare backslash.

Common situations: Paths with spaces quoted on only one side; copy-pasting a Windows path with a stray quote; frontend assembling the string with a broken template that drops the closing quote.

Related errors


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