A Name That Does Not Resolve Is Not Transient

A retry exists to give a transient failure another chance. A hostname that does not exist will not start existing, so retrying it is not resilience — it is three guaranteed failures, three log lines and a delayed answer.

What was measured

memex.systemorph.com, 2026-09-17 (#4613). Two outbound fetches — an agent reading a company's website through the WebSearch plugin's FetchWebPage tool — failed like this:

fail: Polly[3]
  Execution attempt. Source: '-standard//Standard-Retry', Result: 'Name or service not known
  (www.boss-software.ch:443)', Handled: 'True', Attempt: '3'
  System.Net.Http.HttpRequestException: Name or service not known (www.boss-software.ch:443)
   ---> System.Net.Sockets.SocketException (0xFFFDFFFF): Name or service not known

Attempt: '3' is the whole story: the standard predicate handles every HttpRequestException, so each URL was tried three times.

Whose defect this is — measured, not assumed

The issue offered three environmental explanations (cluster DNS, an egress NetworkPolicy, a pod with no route). All three are ruled out from outside the cluster. Resolved from a laptop on the public internet, 2026-09-17:

name answer
www.boss-software.ch NXDOMAIN
www.bosssw.ch NXDOMAIN
boss-software.ch NXDOMAIN
bosssw.ch NXDOMAIN

Neither apex domain exists, so nothing inside the cluster is broken and "confirm whether those hostnames resolve from inside the cluster" never needed asking. Their half is a URL in somebody's data that points at a hostname that does not exist; no code change fixes that, and neither hostname appears anywhere in MeshWeaver, MeshWeaver.Plugins or Memex, so nothing we ship put it there.

Our half, and why it is worth fixing

Three attempts, each logged at Error under category Polly — and Error lines are what the fleet's log watcher folds into a LogIncident and a GitHub ticket. So a bad URL in customer data manufactured a platform defect report. The incident pipeline could not tell "our software is broken" from "somebody typed a hostname that does not exist", and the second is not ours to fix.

Memex.Portal.ServiceDefaults therefore excludes a name-resolution failure from the default pipeline's retry predicate and from its circuit breaker:

Excluding it from ShouldHandle also drops the log level: Polly logs an unhandled outcome far below Error, so the incident stops being manufactured at its source rather than being filtered downstream.

🚨 Exactly one socket error, and the control matters more than the case

SocketError.HostNotFound only.

SocketError.TryAgain (EAI_AGAIN) is a nameserver that failed to answer — genuinely transient, and excluding it would turn a DNS hiccup into a hard failure, which is the opposite defect and a worse one. ConnectionRefused is a host that exists and is not listening: about the service. ConnectionReset, TimedOut and every TLS failure are about the network or the peer. All of them keep being retried exactly as before.

ServiceDefaults.NameDoesNotResolve walks the inner-exception chain, because HttpClient wraps the resolver's SocketException in an HttpRequestException and a handler pipeline can wrap that again. It is pure, and NonexistentHostIsNotRetriedTest asserts it directly — a predicate that can only be exercised through a real failed connection is a predicate nobody checks.

The half this does not reach

The caller is still not told in words it can act on. FetchWebPageCore already draws the right line for an HTTP status — a 404 is reported to the model as an answer rather than raised as a fault — but a dead hostname still arrives as a raw Name or service not known, and the person who owns the data that holds the URL learns nothing at all. That is a separate change in MeshWeaver.AI.WebSearch, and the principle it should follow is the one already written at that call site: a fact about the URL is an answer, not a malfunction.

Reconnecting…
The connection to the server was interrupted. Trying to restore it…
Trying again…
The connection could not be restored. Reloading the page…
The server was updated. Reloading the page to pick up the latest version.