Skip to content

fix(gateway): surface actionable message for local model server connection errors - #86729

Merged
teknium1 merged 3 commits into
NousResearch:mainfrom
Christopher-Schulze:fix/86570-local-model-connection-reply
Aug 16, 2026
Merged

fix(gateway): surface actionable message for local model server connection errors#86729
teknium1 merged 3 commits into
NousResearch:mainfrom
Christopher-Schulze:fix/86570-local-model-connection-reply

Conversation

@Christopher-Schulze

@Christopher-Schulze Christopher-Schulze commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Local model server connection errors now get a specific "not running or unreachable" reply instead of the generic provider-failure catch-all.

Review follow-up: the provider-error gate only matches unambiguous transport failures (refused/reset/aborted, WinError 10061, errno 111, all connection attempts failed). Broader phrases like cannot connect still map to the specific reply once classified, but they no longer open the rewrite gate on user prose.

Related Issue

Fixes #86570

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)

Changes Made

  • gateway/run.py: connection-error reply branch; narrower connection markers in _GATEWAY_PROVIDER_ERROR_SHAPE_RE.
  • Tests for specific reply, generic preservation, auth/rate-limit, prose cannot connect, and reply-only broad phrases.

How to Test

  1. uv run python -m pytest tests/gateway/test_local_model_connection_reply.py -q → 6 passed.
  2. test_prose_cannot_connect_is_not_a_provider_error fails if the gate still treats cannot connect as an envelope.

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/gateway/test_local_model_connection_reply.py -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15 (arm64)

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A

@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages labels Aug 15, 2026

@trevorgordon981 trevorgordon981 left a comment

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.

Good, well-tested fix for #86570. Giving a specific "local model server not running / unreachable" message when the configured endpoint is down is genuinely more actionable than the generic fallback, and the tests are solid (connection-error samples produce the specific reply, non-connection errors keep the generic one, auth/rate-limit branches preserved). Two things worth a look.

1. The connection patterns now double as a provider-error gate, not just a reply selector

_GATEWAY_CONNECTION_ERROR_RE is used in two places: to pick the reply inside _gateway_provider_error_reply, and the same patterns are appended to the _looks_like_gateway_provider_error gate (via the added alternation). That means a message containing e.g. cannot connect / could not connect / connection reset / connection missing can now classify as a "provider error" even when it isn't one. Most of these are specific enough that false positives are unlikely, but connection reset, network is unreachable, and the bare cannot connect / could not connect phrases are generic enough to appear in unrelated user content that flows through the same error-classification path (e.g. a user pasting a network diagnostic, or an agent turn that happens to mention a connection problem). Worth confirming the gate isn't overly broad — the safest split is to keep the reply selector on the full set, but gate _looks_like_gateway_provider_error only on the unambiguous forms (connection refused/reset/aborted, WinError 10061, errno 111, all connection attempts failed).

2. Regex ordering: a connection error that also mentions auth/rate-limit is hijacked

In _gateway_provider_error_reply, the auth and rate-limit branches are checked before the new connection branch. If a connection failure message happens to include authentication or rate limit text (some networking errors do — e.g. a proxy or TLS error that surfaces a 401/429-style message), it returns the auth/rate-limit reply instead of the (more accurate) connection reply. Minor, but the ordering means the new branch is strictly lowest-priority; that's probably fine given the existing conventions, just worth being deliberate about.

3. Minor

  • The winerror 10061 and errno 111 patterns are good, concrete Windows/Linux anchors — nice.

Tests

Good coverage: specific-reply for 7 connection samples, generic preserved for 3 non-connection errors, regex negative cases, and auth/rate-limit branches still win. The gap is finding #1 — no test asserts that a non-error message containing cannot connect (in prose, not as an error) stays out of the provider-error gate.

@Enough1122

Copy link
Copy Markdown
Contributor

AI code review — automated review for reference, author can ignore or act on any point.

fix(gateway): surface actionable message for local model server connection errors

A targeted fix with good regression coverage. A few observations:

  1. gateway/run.py (_gateway_provider_error_reply, ~line 701): the new message says the endpoint "is not running or is unreachable" — accurate for a local model server, but connection errors also occur with hosted/remote providers (transient network blips, DNS, proxy resets). A remote-provider user would be told their endpoint is down when it is a transient outage. Consider narrowing the message to local endpoints (e.g. when the URL host is loopback/private) or using a neutral "could not be reached" phrasing.

  2. gateway/run.py (~lines 703-735): the ~15 connection-error alternatives are now duplicated verbatim in both _GATEWAY_CONNECTION_ERROR_RE and the broader provider-error regex. Any future edit must keep the two copies in sync. Consider composing one shared pattern (e.g. building the broader regex from the connection alternatives) so they cannot drift.

  3. Minor: the regexes match substrings anywhere in the error text, so a non-connection error whose body quotes connection wording (e.g. an HTTP 500 response containing "connection reset by peer") would be classified as a connection error. Ordering (auth → rate-limit → connection) handles the common cases; just noting the residual ambiguity.

@Christopher-Schulze
Christopher-Schulze force-pushed the fix/86570-local-model-connection-reply branch from f6927e8 to d600150 Compare August 15, 2026 19:29
@Christopher-Schulze

Copy link
Copy Markdown
Contributor Author

Thanks — split the gate from the reply selector. Unambiguous transport failures (refused/reset/aborted, WinError 10061, errno 111, all connection attempts failed) still open the rewrite. Broader phrases like cannot connect still map to the specific reply if already classified, but they no longer rewrite user prose. Auth/rate-limit stay higher priority, matching the existing convention.

@trevorgordon981 trevorgordon981 left a comment

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.

Re-reviewing the follow-up commit ("keep broad connection phrases out of the provider-error gate"): my finding #1 is addressed cleanly. You split the gate from the reply selector — _GATEWAY_CONNECTION_ERROR_RE now anchors the _looks_like_gateway_provider_error gate on the unambiguous transport forms (refused/reset/aborted, WinError 10061, errno 111, all connection attempts failed), while the reply selector keeps the broader phrase set for messages already classified as provider errors. That is exactly the right split.

The regression coverage closes the gap I flagged: test_prose_cannot_connect_is_not_a_provider_error pins a prose mention of "cannot connect" out of the provider-error gate, and test_broad_connection_phrases_still_map_once_classified confirms the wider phrase set still routes to the specific reply once classified. Both verified passing here.

Finding #2 (reply-branch ordering, auth/rate-limit before connection) is preserved and now pinned by test_auth_and_rate_limit_preserved — that matches my "probably fine given existing conventions" note, so non-blocking. Approving.

@teknium1
teknium1 merged commit 3a61fa8 into NousResearch:main Aug 16, 2026
45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: Local model server connection errors fall through to unhelpful generic chat reply

5 participants