alibaba/spring-ai-alibaba · error · WebFetchException

Failed after attempts

Error message

Failed after  attempts

What it means

After exhausting all fetch attempts in fetchHtmlWithRetry, WebFetchTool throws WebFetchException('Failed after N attempts'). The no-cause branch (line 270) fires when the loop ended with lastException == null — every attempt returned normally per the catch structure but no usable response was produced — or conceptually marks the generic exhaustion message. It means the URL could not be fetched within the configured retry budget.

Solutions

  1. Verify the URL is reachable from the host with curl or a browser.
  2. Increase maxRetries via the Builder for flaky targets.
  3. Inspect the chained cause (getCause()) to find the real network failure.
  4. Add connectivity/proxy configuration (HTTP_PROXY, VPN, firewall rules) for the runtime.

Example fix

// before
WebFetchTool tool = WebFetchTool.builder(chatClient).build();
// after
WebFetchTool tool = WebFetchTool.builder(chatClient)
    .maxRetries(3)
    .build();
Defensive patterns

Strategy: retry

Validate before calling

URI uri = URI.create(url);
if (uri.getScheme() == null ||
    !(uri.getScheme().equals("http") || uri.getScheme().equals("https"))) {
    throw new IllegalArgumentException("invalid fetch URL: " + url);
}

Try / catch

try {
    String html = webFetchTool.fetch(url);
} catch (WebFetchException e) {
    log.warn("fetch exhausted: {} cause={}", e.getMessage(), e.getCause());
    return fallbackContent(url);
}

Prevention

When it happens

Trigger: Calling WebFetchTool on a URL where all maxRetries+1 attempts fail, and the final recorded exception is null or a WebFetchException already carrying its own cause.

Common situations: Fetching an unreachable or DNS-failing host; misconfigured maxRetries=0 with a flaky network; a proxy or firewall blocking egress from the runtime environment; URL temporarily returning errors until retries run out.

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/61090a6374ae8e26. Report an issue: GitHub.

Appendix: source

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

				return response;
			}
			catch (WebFetchException e) {
				lastException = e;
				if (e.getCause() instanceof InterruptedException) {
					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")

View on GitHub (pinned to f82da0b50f)