alibaba/spring-ai-alibaba · error · WebFetchException

Failed after attempts:

Error message

Failed after  attempts: 

What it means

Final branch of fetchHtmlWithRetry: when the last exception is not a WebFetchException (e.g. a raw IOException or other RuntimeException from the fetch), it is wrapped in WebFetchException with 'Failed after N attempts: <cause message>'. This preserves the original error text in the message and the exception in the cause.

Source

Thrown at spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tools/WebFetchTool.java:276

					throw e;
				}
				logger.warn("Fetch attempt {} failed for URL: {}: {}", attempt + 1, url, e.getMessage());
				attempt++;
			}
			catch (InterruptedException e) {
				Thread.currentThread().interrupt();
				throw new WebFetchException("Retry interrupted", e);
			}
		}

		if (lastException == null) {
			throw new WebFetchException("Failed after " + (this.maxRetries + 1) + " attempts", null);
		}
		else if (lastException instanceof WebFetchException) {
			throw new WebFetchException("Failed after " + (this.maxRetries + 1) + " attempts", lastException);
		}
		else {
			throw new WebFetchException(
					"Failed after " + (this.maxRetries + 1) + " attempts: " + lastException.getMessage(),
					lastException);
		}
	}

	private HttpResponse<String> fetchHtml(String url) {
		HttpRequest request = HttpRequest.newBuilder()
			.uri(URI.create(url))
			.timeout(DEFAULT_REQUEST_TIMEOUT)
			.header("User-Agent", USER_AGENT)
			.header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
			.header("Accept-Language", "en-US,en;q=0.5")
			.GET()
			.build();

		try {
			HttpResponse<byte[]> byteResponse = this.httpClient.send(request,
					HttpResponse.BodyHandlers.ofByteArray());

View on GitHub (pinned to f82da0b50f)

Solutions

  1. Parse the ': <message>' suffix or getCause() to find the concrete root error.
  2. Validate/normalize the URL before calling the tool (proper scheme, no illegal characters).
  3. Check TLS/SSL truststore configuration if the cause mentions certificates.
  4. Enable logging around the tool call to capture the full stack of the wrapped cause.

Example fix

// before
String html = tool.fetch(userUrl);
// after
URI uri = URI.create(userUrl);
if (!"http".equals(uri.getScheme()) && !"https".equals(uri.getScheme())) {
    throw new IllegalArgumentException("URL must be http(s): " + userUrl);
}
String html = tool.fetch(uri.toString());
Defensive patterns

Strategy: try-catch

Validate before calling

URI uri = URI.create(url);
if (!"https".equals(uri.getScheme()) && !"http".equals(uri.getScheme())) {
    throw new IllegalArgumentException("URL must use http/https: " + url);
}

Try / catch

try {
    String html = webFetchTool.fetch(url);
} catch (WebFetchException e) {
    String rootMessage = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();
    log.error("fetch failed after retries: {}", rootMessage, e);
    return null;
}

Prevention

When it happens

Trigger: Retry loop exhausted with a non-WebFetchException as lastException — for example an unchecked exception thrown inside fetchHtml or an IOException path not converted earlier.

Common situations: Unexpected runtime errors during fetch (malformed URL passed to HttpClient, SSL handshake exceptions surfaced by the underlying client); target host rejecting TLS.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


AI-assisted analysis of alibaba/spring-ai-alibaba@f82da0b50f (2026-09-09). Data as JSON: /api/errors/a492cb125c4b9ac6. Report an issue: GitHub.