Skip to content

fix(acp): surface serve-acp startup failures on stderr instead of silent exit - #407

Open
Million-mo wants to merge 1 commit into
mainfrom
fix/acp-startup-silent-exit
Open

fix(acp): surface serve-acp startup failures on stderr instead of silent exit#407
Million-mo wants to merge 1 commit into
mainfrom
fix/acp-startup-silent-exit

Conversation

@Million-mo

Copy link
Copy Markdown
Collaborator

Problem

wolfharness serve-acp could exit with no terminal output when startup failed, making server startup failures indistinguishable from success.

Root cause (three layers)

  1. serve_acp.py redirects all logging to the log file via configure_logging(force=True, log_file=...)basicConfig(force=True) removes the stderr handler, so every subsequent logger.exception only lands in ~/Library/Logs/wolfharness/acp.log.
  2. ACPServer._start_async swallows serve-loop exceptions (except Exception: self.log.exception(...)) and never re-raises, despite being constructed with raise_exceptions=True (contradicting BaseServer.start semantics) — serve errors (e.g. port already in use) exited the process with code 0 silently.
  3. The CLI handler serve_acp.py caught the startup error and raised typer.Exit(1), which click handles by exiting without printing anything.

Reproduced with a config that passes validation but fails at agent construction (model: nosuchprovider:nonexistent): process exited with code 1, empty stdout and stderr; the traceback existed only in the log file.

Fix

  • ACPServer._start_async now re-raises serve errors when raise_exceptions is set, consistent with BaseServer.start — serve failures propagate to the CLI handler instead of dying silently.
  • serve_acp now prints the exception type/message and the log file path to stderr before exiting with code 1.

Tests

  • Unit tests: _start_async re-raises when raise_exceptions=True and swallows-with-log when False.
  • E2E test: spawns the real CLI with a broken-model config and asserts the error appears on stderr with exit code 1 — the exact reported scenario.

Verification

  • New tests pass; 333 existing server/cli tests pass; ruff + mypy clean.
  • Manual repro: stderr now shows ACP server failed to start: ValueError: Unknown provider: nosuchprovider + Full traceback in log file: .../acp.log, exit 1.

Closes the silent-startup-exit report.

…ent exit

serve-acp redirected all logging to the log file and swallowed startup
exceptions: serve-loop errors in ACPServer._start_async were never
re-raised despite raise_exceptions=True, and the CLI handler exited with
a silent typer.Exit(1). A config that passed validation but failed at
agent or transport startup therefore exited with code 1 (or 0) and no
terminal output.

- _start_async now re-raises serve errors when raise_exceptions is set,
  consistent with BaseServer.start.
- serve_acp now prints the exception type/message and the log file path
  to stderr before exiting with code 1.

Adds unit tests for the re-raise/suppress branch and an e2e test asserting
the real CLI prints the error on stderr.
@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown

Model not found: deepseek/deepseek-v4-flash. Did you mean: deepseek-v4-flash, deepseek-v4-flash-vision-exp?

opencode session  |  github run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant