Skip to content

Bug: Startup crash when OpenCode agent profile is auto-detected — cptr tries to JSON-parse opencode serve's plain-text stdout #222

Description

@Rory-Lambert

cptr (installed via uvx cptr@latest run) crashes on startup with an unhandled JSONDecodeError when it auto-detects a locally installed opencode CLI and probes it as an agent profile. This kills the entire FastAPI app during lifespan startup, not just that one profile.

Environment:

  • Arch Linux
  • opencode installed via pacman/AUR, fully functional and logged in
  • cptr run via uvx cptr@latest run
  • Python 3.14 (via uv's resolved interpreter) — also reproduced when pinning --python 3.12, so not Python-version-specific

Traceback:
File ".../cptr/app.py", line 67, in lifespan
await warm_model_cache(app.state)
File ".../cptr/routers/chat.py", line 281, in warm_model_cache
await asyncio.gather(*tasks)
File ".../cptr/utils/agents/detection.py", line 690, in get_available_agent_model_entries
status = await get_agent_status(app_state)
File ".../cptr/utils/agents/detection.py", line 648, in get_agent_status
detected = await detect_profile(profile)
File ".../cptr/utils/agents/detection.py", line 267, in detect_profile
models = await _probe_acp_models(command, profile, auth_method_id="oauth-personal")
File ".../cptr/utils/agents/detection.py", line 589, in _probe_acp_models
await client.close()
File ".../cptr/utils/agents/acp.py", line 85, in close
await task
File ".../cptr/utils/agents/acp.py", line 183, in _reader_loop
extracted = _extract_json_message(buffer)
File ".../cptr/utils/agents/acp.py", line 261, in _extract_json_message
return json.loads(line.decode()), buffer[line_end + 1 :]
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

Root cause (confirmed via manual reproduction):
For the OpenCode profile type, cptr spawns:
opencode serve --hostname=127.0.0.1 --port=
Running that exact command by hand shows opencode writes plain human-readable log lines to stdout on startup, not JSON:
Warning: OPENCODE_SERVER_PASSWORD is not set; server is unsecured.
opencode server listening on http://127.0.0.1:47645
cptr's _reader_loop/_extract_json_message appears to treat this subprocess's stdout as a newline-delimited JSON-RPC stream (the ACP convention used for other agent types), and immediately fails json.loads() on the first non-JSON line. This is reproducible even with a no-op stub binary named opencode on PATH that produces zero stdout output before exiting — json.loads("") throws the identical error, confirming this isn't about opencode's specific output but about cptr expecting JSON where serve mode never produces it.

Impact: Any user with opencode installed and discoverable on PATH cannot start cptr at all — a single bad profile probe takes down the entire app during lifespan startup (asyncio.gather(*tasks) in warm_model_cache has no per-task error isolation, and detect_profile/_probe_acp_models don't catch JSONDecodeError).

Suggested fixes:

  1. Catch and isolate per-profile detection errors in warm_model_cache/get_available_agent_model_entries so one broken agent profile can't crash app startup.
  2. For the OpenCode profile type specifically, don't reuse the ACP JSON-RPC stdout reader against opencode serve — its stdout is plain log text, not JSON-RPC; communication should happen over HTTP against the bound port instead.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions