{"record":{"id":"61090a6374ae8e26","repo":"alibaba/spring-ai-alibaba","slug":"failed-after-attempts","errorCode":null,"errorMessage":"Failed after  attempts","messagePattern":"Failed after  attempts","errorType":"exception","errorClass":"WebFetchException","httpStatus":null,"severity":"error","filePath":"spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tools/WebFetchTool.java","lineNumber":270,"sourceCode":"\n\t\t\t\treturn response;\n\t\t\t}\n\t\t\tcatch (WebFetchException e) {\n\t\t\t\tlastException = e;\n\t\t\t\tif (e.getCause() instanceof InterruptedException) {\n\t\t\t\t\tthrow e;\n\t\t\t\t}\n\t\t\t\tlogger.warn(\"Fetch attempt {} failed for URL: {}: {}\", attempt + 1, url, e.getMessage());\n\t\t\t\tattempt++;\n\t\t\t}\n\t\t\tcatch (InterruptedException e) {\n\t\t\t\tThread.currentThread().interrupt();\n\t\t\t\tthrow new WebFetchException(\"Retry interrupted\", e);\n\t\t\t}\n\t\t}\n\n\t\tif (lastException == null) {\n\t\t\tthrow new WebFetchException(\"Failed after \" + (this.maxRetries + 1) + \" attempts\", null);\n\t\t}\n\t\telse if (lastException instanceof WebFetchException) {\n\t\t\tthrow new WebFetchException(\"Failed after \" + (this.maxRetries + 1) + \" attempts\", lastException);\n\t\t}\n\t\telse {\n\t\t\tthrow new WebFetchException(\n\t\t\t\t\t\"Failed after \" + (this.maxRetries + 1) + \" attempts: \" + lastException.getMessage(),\n\t\t\t\t\tlastException);\n\t\t}\n\t}\n\n\tprivate HttpResponse<String> fetchHtml(String url) {\n\t\tHttpRequest request = HttpRequest.newBuilder()\n\t\t\t.uri(URI.create(url))\n\t\t\t.timeout(DEFAULT_REQUEST_TIMEOUT)\n\t\t\t.header(\"User-Agent\", USER_AGENT)\n\t\t\t.header(\"Accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\")\n\t\t\t.header(\"Accept-Language\", \"en-US,en;q=0.5\")","sourceCodeStart":252,"sourceCodeEnd":288,"githubUrl":"https://github.com/alibaba/spring-ai-alibaba/blob/f82da0b50f35744c13968191be2b1cd2452ef550/spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/tools/WebFetchTool.java#L252-L288","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the URL is reachable from the host with curl or a browser.","Increase maxRetries via the Builder for flaky targets.","Inspect the chained cause (getCause()) to find the real network failure.","Add connectivity/proxy configuration (HTTP_PROXY, VPN, firewall rules) for the runtime."],"exampleFix":"// before\nWebFetchTool tool = WebFetchTool.builder(chatClient).build();\n// after\nWebFetchTool tool = WebFetchTool.builder(chatClient)\n    .maxRetries(3)\n    .build();","handlingStrategy":"retry","validationCode":"URI uri = URI.create(url);\nif (uri.getScheme() == null ||\n    !(uri.getScheme().equals(\"http\") || uri.getScheme().equals(\"https\"))) {\n    throw new IllegalArgumentException(\"invalid fetch URL: \" + url);\n}","typeGuard":null,"tryCatchPattern":"try {\n    String html = webFetchTool.fetch(url);\n} catch (WebFetchException e) {\n    log.warn(\"fetch exhausted: {} cause={}\", e.getMessage(), e.getCause());\n    return fallbackContent(url);\n}","preventionTips":["Verify URL reachability (curl/DNS) before wiring it into the agent","Configure an adequate maxRetries for flaky targets","Ensure outbound network/proxy access from the runtime environment"],"tags":["http","retry","network"],"backgroundTag":"http-request-failed","analyzedSha":"f82da0b50f35744c13968191be2b1cd2452ef550","analyzedAt":"2026-09-09T15:32:42.421Z","contentChangedAt":"2026-09-09T15:32:42.421Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}