{"record":{"id":"a492cb125c4b9ac6","repo":"alibaba/spring-ai-alibaba","slug":"failed-after-attempts-a492cb","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":276,"sourceCode":"\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\")\n\t\t\t.GET()\n\t\t\t.build();\n\n\t\ttry {\n\t\t\tHttpResponse<byte[]> byteResponse = this.httpClient.send(request,\n\t\t\t\t\tHttpResponse.BodyHandlers.ofByteArray());","sourceCodeStart":258,"sourceCodeEnd":294,"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#L258-L294","documentation":"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.","triggerScenarios":"Retry loop exhausted with a non-WebFetchException as lastException — for example an unchecked exception thrown inside fetchHtml or an IOException path not converted earlier.","commonSituations":"Unexpected runtime errors during fetch (malformed URL passed to HttpClient, SSL handshake exceptions surfaced by the underlying client); target host rejecting TLS.","solutions":["Parse the ': <message>' suffix or getCause() to find the concrete root error.","Validate/normalize the URL before calling the tool (proper scheme, no illegal characters).","Check TLS/SSL truststore configuration if the cause mentions certificates.","Enable logging around the tool call to capture the full stack of the wrapped cause."],"exampleFix":"// before\nString html = tool.fetch(userUrl);\n// after\nURI uri = URI.create(userUrl);\nif (!\"http\".equals(uri.getScheme()) && !\"https\".equals(uri.getScheme())) {\n    throw new IllegalArgumentException(\"URL must be http(s): \" + userUrl);\n}\nString html = tool.fetch(uri.toString());","handlingStrategy":"try-catch","validationCode":"URI uri = URI.create(url);\nif (!\"https\".equals(uri.getScheme()) && !\"http\".equals(uri.getScheme())) {\n    throw new IllegalArgumentException(\"URL must use http/https: \" + url);\n}","typeGuard":null,"tryCatchPattern":"try {\n    String html = webFetchTool.fetch(url);\n} catch (WebFetchException e) {\n    String rootMessage = e.getCause() != null ? e.getCause().getMessage() : e.getMessage();\n    log.error(\"fetch failed after retries: {}\", rootMessage, e);\n    return null;\n}","preventionTips":["Sanitize/normalize URLs (scheme, encoding) before fetching","Keep JVM CA certificates updated to avoid TLS-related wrapped failures","Enable debug logging on java.net.http to see raw client errors"],"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-14T11:17:12.474Z"}