Skip to content

Commit 1334b61

Browse files
committed
Capture stderr from the dedicated WM server startup
start_own_server() (used by prepare-envs, run, dump-config, bootstrap) sent the subprocess's stderr to DEVNULL, so a crash before the server's own logger initializes left no trace anywhere. The resulting timeout error told the caller to "check logs for errors" that never existed. Capture stderr to a file instead (creating its parent dir, which ensure_running gets for free but this path doesn't), and inline its content into the raised error so it survives even when the disk that held it is gone by the time anyone looks (e.g. a CI runner). Claude-Session: https://claude.ai/code/session_01XaMRTq9Sk67d8x8zEm7wBJ
1 parent 3ba945a commit 1334b61

1 file changed

Lines changed: 30 additions & 9 deletions

File tree

src/finecode/wm_server/wm_lifecycle.py

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,11 @@ def start_own_server(
180180
# Write empty content so the server overwrites rather than appends.
181181
port_file.write_text("")
182182

183+
stderr_path = startup_stderr_log_path()
184+
# Unlike `ensure_running`, nothing else guarantees this directory exists
185+
# first (there `_startup_lock()` creates it as a side effect before the
186+
# log file is opened) — on a first-ever start in this venv it is missing.
187+
stderr_path.parent.mkdir(parents=True, exist_ok=True)
183188
logger.info(f"Starting dedicated FineCode WM server in {workdir}")
184189
command = [
185190
sys.executable,
@@ -196,13 +201,17 @@ def start_own_server(
196201

197202
# No own session, unlike `ensure_running`: a dedicated server belongs to
198203
# exactly one client and is reachable only through that client's port file,
199-
# so outliving it would leave a ghost nobody can find.
200-
subprocess.Popen(
201-
command,
202-
cwd=str(workdir),
203-
stdout=subprocess.DEVNULL,
204-
stderr=subprocess.DEVNULL,
205-
)
204+
# so outliving it would leave a ghost nobody can find. stderr is still
205+
# captured to a file (not DEVNULL'd) so a crash before the server's own
206+
# logger is initialized is not silently lost — see `wait_until_ready_from_file`,
207+
# whose error message points here.
208+
with open(stderr_path, "w") as stderr_file:
209+
subprocess.Popen(
210+
command,
211+
cwd=str(workdir),
212+
stdout=subprocess.DEVNULL,
213+
stderr=stderr_file,
214+
)
206215
return port_file
207216

208217

@@ -289,7 +298,19 @@ async def wait_until_ready_from_file(
289298
except (FileNotFoundError, ValueError, OSError):
290299
pass
291300
await asyncio.sleep(0.5)
301+
stderr_path = startup_stderr_log_path()
302+
# Inlined, not just referenced by path: on CI the runner's disk is gone by
303+
# the time anyone could go look, so the message itself is the only place
304+
# this content is ever seen.
305+
try:
306+
stderr_tail = stderr_path.read_text().strip()
307+
except OSError:
308+
stderr_tail = ""
309+
detail = (
310+
f"Captured stderr ({stderr_path}):\n{stderr_tail}"
311+
if stderr_tail
312+
else f"{stderr_path} is empty — the process is still starting, not crashing."
313+
)
292314
raise TimeoutError(
293-
f"Dedicated FineCode WM server did not start within {timeout}s. "
294-
"Check logs for errors."
315+
f"Dedicated FineCode WM server did not start within {timeout}s.\n{detail}"
295316
)

0 commit comments

Comments
 (0)