Skip to content

fix(clients): stop hanging when the local server never accepts connections - #10611

Open
leilei3167 wants to merge 1 commit into
stanfordnlp:mainfrom
leilei3167:fix/issue-10609-local-launch-timeout
Open

leilei3167 wants to merge 1 commit into
stanfordnlp:mainfrom
leilei3167:fix/issue-10609-local-launch-timeout

Conversation

@leilei3167

Copy link
Copy Markdown

📝 Changes Description

Fixes #10609.

LocalProvider.launch never returned when the port refused connections. wait_for_server checked timeout only after a non-200 response. A closed port makes requests.get raise RequestException, and that branch only slept, so the deadline never fired and process.kill() in launch was never reached. A server process that exits during startup leaves the caller waiting the same way.

wait_for_server now checks the deadline on that error path, and raises TimeoutError as soon as the server process has exited. launch kills the process on any exception from the wait when it is still running.

The new tests in tests/clients/test_lm_local.py cover a closed port with timeout=1, a process that has already exited, and a process that stays up but never accepts a connection. Before this change those calls were still running after 6 seconds. After the change, pytest tests/clients/test_lm_local.py reports 6 passed, and ruff check --fix-only --diff on the two files is clean.

✅ Contributor Checklist

  • Pre-Commit checks are passing (locally and remotely)
  • Title of your PR / MR corresponds to the required format
  • Commit message follows required format {label}(dspy): {message}

⚠️ Warnings

No GPU or SGLang install is required for the new tests. They stop after 6 seconds so a hang fails the test instead of stalling the job.

@greptile-apps

greptile-apps Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerConfidence Score: 4/5

[Medium impact] Changes how the local server startup waits for readiness.

The PR is safe to merge with one non-blocking test-isolation concern: a hung worker can retain a fake module and affect later tests.

Findings

  1. P2 Hung workers leave mocks installed ▶

T-Rex evidence

Executed controlled-hang verification script

  • The script invokes the original decorated test in non-hanging and hanging modes, checks module visibility, and releases the worker for cleanup; it reproduces the candidate without changing tracked code.

Non-hanging launch control

  • Ran the original test with an immediate controlled readiness timeout; the worker stopped, the test passed, and no fake sglang module remained.

Hung launch leaves the module mock installed

  • Blocked readiness past the shortened watchdog and imported sglang before releasing the worker; the import returned the mock after watchdog return and the mock persisted through test failure, confirming contamination.

Evidence from the check

  • The script invokes the original decorated test in non-hanging and hanging modes, checks module visibility, and releases the worker for cleanup; it reproduces the candidate without changing tracked code.

Command output from the check

  • Ran the original test with an immediate controlled readiness timeout; the worker stopped, the test passed, and no fake sglang module remained.

Command output from the check

  • Blocked readiness past the shortened watchdog and imported sglang before releasing the worker; the import returned the mock after watchdog return and the mock persisted through test failure, confirming contamination.

View artifacts

Summary

The PR stops startup polling when a closed port outlasts the deadline or the server process has exited.

  • Local model launches stop waiting when the server cannot start.

Reviews (1) · Last reviewed commit: "fix(dspy): stop hanging when the local s..." · Reviewed by Greptile

Comment on lines +176 to +178
def launch():
with mock.patch.dict("sys.modules", {"sglang": mock.Mock()}):
LocalProvider.launch(lm, launch_kwargs={"timeout": 1})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Hung workers leave mocks installed

When the six-second watchdog expires, _outcome_within returns without stopping its worker. That worker still holds the sys.modules patch open until LocalProvider.launch finishes. Even after the test fails, later tests can therefore import the fake sglang module and fail for unrelated reasons.

This is a non-blocking test-isolation concern. Keep the module patch in the main test's scope so it is restored on failure, or run the potentially hanging launch in a separate process that the watchdog can terminate and join.

Artifacts

Executed controlled-hang verification script

  • The script invokes the original decorated test in non-hanging and hanging modes, checks module visibility, and releases the worker for cleanup; it reproduces the candidate without changing tracked code.

Non-hanging launch control

  • Ran the original test with an immediate controlled readiness timeout; the worker stopped, the test passed, and no fake sglang module remained.

Hung launch leaves the module mock installed

  • Blocked readiness past the shortened watchdog and imported sglang before releasing the worker; the import returned the mock after watchdog return and the mock persisted through test failure, confirming contamination.

View artifacts

T-Rex Ran code and verified through T-Rex

This branch has not been deployed

No deployments
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.

LocalProvider.launch hangs forever when the server never accepts connections

1 participant