Quieten expected Platform-connectivity errors in the token watcher#1085
Merged
Conversation
When the workflow-status lookup fails because the run's Platform endpoint is
briefly unreachable (e.g. a paired self-hosted instance whose pairing
WebSocket times out), the watcher caught it in the generic catch-all and
logged a full ERROR stack trace on every retry. For chronically-unreachable
paired endpoints this floods the logs and inflates error-rate signals, even
though the behavior is correct (the token is not renewed and lapses
fail-closed, then the entry stops on the next 'gone' check).
Distinguish these transient connectivity failures (TimeoutException /
HttpResponseException in the cause chain) from genuinely unexpected errors:
log the former as a concise one-line warning, keep the ERROR+stack trace for
the latter. Retry and the wave.tokens.refresh{result=error} counter are
unchanged for both.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pditommaso
force-pushed
the
fix-watcher-connectivity-logging
branch
from
July 16, 2026 15:21
7dc45ad to
12be528
Compare
gavinelder
reviewed
Jul 16, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
|
@pditommaso The nested loops still don't look correct and the I cannot do code suggestions over multiple lines however if you could look at something like the following instead , i just tested it on the codebase and tests appear to be passing. Testing with Claude it gives the following feedback
private static final int MAX_CAUSE_DEPTH = 50
/**
* Whether the given error is an expected Platform connectivity failure (a timeout or an HTTP
* error reaching Tower) as opposed to an unexpected bug. Such failures are transient and retried,
* so they should not be logged at ERROR with a full stack trace. The cause chain is walked up to
* MAX_CAUSE_DEPTH to guard against cyclical cause chains.
*/
protected static boolean isConnectivityError(Throwable t) {
Throwable e = t
for( int i = 0; e != null && i < MAX_CAUSE_DEPTH; e = e.cause, i++ ) {
if( e instanceof TimeoutException || e instanceof HttpResponseException )
return true
}
return false
}
protected static String rootCauseMessage(Throwable t) {
if( t == null )
return null
Throwable e = t
int i = 0
while( e.cause != null && e.cause != e && i++ < MAX_CAUSE_DEPTH )
e = e.cause
return e.message ?: e.class.simpleName
} |
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Collaborator
Author
|
Agreed on all three — applied in 5842660: dropped the redundant |
Collaborator
Author
|
Great, thanks Gavin! 🙏 All applied. |
gavinelder
approved these changes
Jul 16, 2026
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.
Problem
Observed in prod after the #782 rollout: when the token watcher's
describeWorkflowlookup fails because the run's Platform endpoint is briefly unreachable — e.g. a paired self-hosted instance whose pairing WebSocket times out (HTTP 408 /TimeoutExceptiononpairing-inbound-queue) — the exception propagated to the generic catch-all inwatch()and was logged at ERROR with a full stack trace on every retry.The behavior is already correct (the token is not renewed → lapses fail-closed → next check sees
goneand stops tracking), so the stack trace is pure noise. For a chronically-unreachable paired endpoint it repeats everyrefresh-intervalper token, floods the logs, and inflates error-rate signals.Change
In the watcher's catch-all, distinguish an expected Platform-connectivity failure (
TimeoutExceptionorHttpResponseExceptionanywhere in the cause chain) from a genuinely unexpected error:WARN(request id, workflow id, root-cause message), no stack trace;ERROR+ stack trace.Retry (
scheduleRefresh) and thewave.tokens.refresh{result=error}counter are unchanged for both paths, so the metric-based failure signal is preserved — only the log verbosity changes.Tests
Added a data-driven spec for
isConnectivityError(wrappedTimeoutException/HttpResponseException/ExecutionException→ true; plainRuntimeException/ NPE → false) androotCauseMessage.🤖 Generated with Claude Code