Skip to content

Lemonade: subprocess.Popen(list, shell=True) drops arguments on POSIX #787

@kovtcharov

Description

@kovtcharov

Summary

src/gaia/llm/lemonade_client.py calls subprocess.Popen with both a list of args and shell=True. On POSIX this is a documented pitfall — only the first list element is treated as the shell command, and the remaining items become positional args to sh itself (not to the command), silently dropping serve, --log-level <level>, --ctx-size <N>, etc.

Locations

base_cmd = ["lemonade-server", "serve"]
if log_level != "info":
    base_cmd.extend(["--log-level", log_level])
if ctx_size is not None:
    base_cmd.extend(["--ctx-size", str(ctx_size)])
...
self.server_process = subprocess.Popen(
    base_cmd,               # ← list
    ...
    shell=True,             # ← + shell=True = POSIX argument loss
)

Expected

All arguments are forwarded to lemonade-server.

Actual

  • Windows: Happens to work because Python stringifies the list via list2cmdline() before handing it to cmd.exe.
  • POSIX (Linux/macOS): sh -c 'lemonade-server' 'serve' '--log-level' 'debug' ... — the shell runs lemonade-server with no arguments; serve/--log-level/--ctx-size are passed to sh as \$0, \$1, etc.

Suggested Fix

Drop shell=True when passing a list:

self.server_process = subprocess.Popen(
    base_cmd,
    stdout=self._log_file,
    stderr=self._log_file,
    text=True,
    bufsize=1,
    # shell=True removed — args must reach lemonade-server verbatim
)

Verify Windows path still works; if cmd.exe quoting was needed there, fall back to shell=True with \" \".join(base_cmd) (a string, not a list).

Impact

  • Launching Lemonade from gaia on Linux silently ignores --ctx-size and --log-level, leading to wrong context size and hidden logging regressions that only bite on POSIX CI/dev machines.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions