fix(cron): halt Sentry flood when a local LLM provider is offline (#4408)#4415
Merged
senamakel merged 2 commits intoJul 3, 2026
Merged
Conversation
…humansai#4408) is_loopback_unavailable required the English 'connection refused (os error N)' string, so a non-English-locale host (zh-CN Windows renders WSAECONNREFUSED translated) leaked past it into the broad NetworkUnreachable bucket. Add a locale-independent arm keyed on the transport-stable 'tcp connect error' marker plus the connect-refused errno, and expose is_local_provider_unreachable_message as a single-source wrapper for the cron retry-halt guard.
…ine (tinyhumansai#4408) A cron agent job pinned to a local LLM provider (LM Studio on localhost:1234) whose server isn't running fails with a loopback connection-refused. The provider/agent emit sites already demote this via the expected_error_kind classifier (LoopbackUnavailable), but execute_job_with_retry burns every retry then fires a bare report_error(failure=retries_exhausted) that bypasses the classifier — 2802 events / 29 users (TAURI-RUST-12K). Add a 5th retry-halt guard mirroring the existing 401/402/400/api-key guards: halt on the first occurrence and skip the bypassing report, keyed on the shared loopback matcher so it stays narrow (remote/transient errors still retry and still report). Modeled on the session_expired halt (no UserErrorCenter swap). Covers the in-loop halt path with predicate + end-to-end integration tests.
Contributor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (3)
👮 Files not reviewed due to content moderation or server errors (3)
📝 Walkthrough
Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
agent_jobfrom flooding Sentry (2802 events / 29 users, TAURI-RUST-12K) when it runs against a local LLM provider (LM Studio onlocalhost:1234) whose server isn't running.execute_job_with_retryfor a loopback connection-refused — halt on first occurrence and skip the bypassing report, mirroring the existing 401 / 402 / 400 / api-key guards.is_loopback_unavailable: it only matched the English "connection refused (os error N)" string, so non-English-locale hosts (this crash: a zh-CN Windows box) misrouted to the broadNetworkUnreachablebucket.Problem
A cron agent job (e.g.
morning_briefing) pinned to a keyless local provider fails at the TCP layer witherror sending request for url (http://localhost:1234/v1/chat/completions): … tcp connect error … (os error 10061)— the user's local model server is offline.The provider/agent emit sites already demote this via the
expected_error_kindclassifier (LoopbackUnavailable), so it never reaches Sentry there. Butexecute_job_with_retryburns every retry and then fires a barereport_error(failure=retries_exhausted)that bypasses the classifier demotion — exactly the leak already fixed for billing (#3913) and api-key (#4166) states, just for a different error class. Result: 2802 events / 29 users for a condition the app has no lever over.Secondary:
is_loopback_unavailablerequired the literal Englishconnection refused (os error N). On a non-English OS the kernel rendersWSAECONNREFUSED/ECONNREFUSEDtranslated, so a genuine loopback-refused body leaked into the broaderNetworkUnreachablebucket, blurring "local server down" vs "user network problem".Solution
observability.rs— add a locale-independent arm tois_loopback_unavailable: loopback host + the transport-stabletcp connect errormarker + the connect-refused errno (61/111/10061). The distinct timeout errnos (60/110/10060) are deliberately not matched. Exposeis_local_provider_unreachable_messageas a single-source wrapper so the cron guard can't drift from the classifier.scheduler.rs— addis_local_provider_unreachable_failure(Agent-only,last_agent_error-first) and alocal_unreachableflag; halt on the first occurrence and gate out the terminal report. Narrow by design — only loopback connection-refused matches, so a transient remote provider/backend error still retries and still reports. Modeled on thesession_expiredhalt (no UserErrorCenter swap); the failure still surfaces via run history + cron alert.S3.5 bucket: genuinely-unpreventable — the app has no lever to start a user's local model server, and the classifier already deems this kind expected at every other emit site. This is a consistency fix (the lone cron path that bypassed the existing demotion), not new suppression, and not on a data path.
Submission Checklist
diff-cover(cargo-llvm-cov lcov vsupstream/main) = 95% (observability.rs100%,scheduler.rs88%). All new logic is covered incl. the end-to-end integration test that drivesexecute_job_with_retryto a real loopback halt. The 3 "missing"scheduler.rslines are pre-existing sibling-guard branches (credits/budget log arg + the non-halt terminal condition) exercised only by the credits/budget/exhaust tests that this focused run's filter excluded — green under the full CI suite.N/A: behaviour-only change(Sentry-noise suppression + classifier fix; no new feature row)## RelatedN/A: does not change a release-cut surfaceCloses #NNNin the## RelatedsectionImpact
Related
AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
fix/4408-cron-local-provider-unreachable7c63e2e37(observability),1fd148d84(cron)Validation Run
cargo test --lib local_provider(14 passed: 5 guard predicate + classifier arms + 1 end-to-end integration)cargo fmt --checkclean;cargo checkclean;cargo clippy --libclean (pre-existing warning only)Validation Blocked
command:N/Aerror:N/Aimpact:N/ABehavior Changes
failure=retries_exhaustedSentry event.Parity Contract
retries_exhausted; the 4 existing halt guards are unchanged.JobType::Agent-only and keyed on the shared classifier matcher; negative tests assert remote-host refused + shell jobs do NOT halt.Duplicate / Superseded PR Handling