isTransientNetworkError function

bool isTransientNetworkError(
  1. AssistantMessage message
)

Whether message is a transient failure worth replaying: socket-level drops and gateway 5xx. Rate limits stay with the roles layer (the FallbackStreamFunction rotation policy — a 429 may quote "please try again later"), budget/spending exhaustion is terminal and wins over every net below it (issue #926 — a gateway-wrapped budget error quotes "500 … please try again later"), auth failures stand, context overflow belongs to compaction, and the idle watchdog's own TimeoutException wording deliberately does NOT match (that error means "the endpoint went silent", which a retry re-arms anyway) — while the connect watchdog's zero-byte wording DOES via connectWatchdogTag (issue #1121: a never-started request is replayed, a silent mid-stream one is not).

Implementation

bool isTransientNetworkError(AssistantMessage message) {
  if (message.stopReason != StopReason.error) return false;
  final text = message.errorMessage;
  if (text == null || text.isEmpty) return false;
  if (text.toLowerCase().contains('certificate')) return false;
  if (isContextOverflow(message)) return false;
  if (isBudgetExhaustion(message)) return false;
  // A resumed-exhaustion terminal (issue #1168) is a consumed verdict:
  // its per-attempt evidence quotes raw transport wordings, but the
  // chain already spent its budget - upper layers must neither rewrap
  // ("not retried" would be false) nor rotate on a false pretext.
  if (isTransientExhaustionStory(text)) return false;
  if (_rateLimitGuardPatterns.any((pattern) => pattern.hasMatch(text))) {
    return false;
  }
  return _transientNetworkPatterns.any((pattern) => pattern.hasMatch(text));
}