{"record":{"id":"a68aac1250192366","repo":"chinabugotech/hutool","slug":"failed-to-send-get-request","errorCode":null,"errorMessage":"Failed to send GET request: ","messagePattern":"Failed to send GET request: ","errorType":"exception","errorClass":"AIException","httpStatus":null,"severity":"error","filePath":"hutool-ai/src/main/java/cn/hutool/ai/core/BaseAIService.java","lineNumber":70,"sourceCode":"\t/**\n\t * 发送Get请求\n\t * @param endpoint 请求节点\n\t * @return 请求响应\n\t */\n\tprotected HttpResponse sendGet(final String endpoint) {\n\t\t//链式构建请求\n\t\ttry {\n\t\t\t//设置超时3分钟\n\t\t\tHttpRequest httpRequest = HttpRequest.get(config.getApiUrl() + endpoint)\n\t\t\t\t.header(Header.ACCEPT, \"application/json\")\n\t\t\t\t.header(Header.AUTHORIZATION, \"Bearer \" + config.getApiKey())\n\t\t\t\t.timeout(config.getTimeout());\n\t\t\tif (config.getHasProxy()) {\n\t\t\t\thttpRequest.setProxy(config.getProxy());\n\t\t\t}\n\t\t\treturn httpRequest.execute();\n\t\t} catch (final Exception e) {\n\t\t\tthrow new AIException(\"Failed to send GET request: \" + e.getMessage(), e);\n\t\t}\n\t}\n\n\t/**\n\t * 发送Post请求\n\t * @param endpoint 请求节点\n\t * @param paramJson 请求参数json\n\t * @return 请求响应\n\t */\n\tprotected HttpResponse sendPost(final String endpoint, final String paramJson) {\n\t\t//链式构建请求\n\t\ttry {\n\t\t\tHttpRequest httpRequest = HttpRequest.post(config.getApiUrl() + endpoint)\n\t\t\t\t.header(Header.CONTENT_TYPE, \"application/json\")\n\t\t\t\t.header(Header.ACCEPT, \"application/json\")\n\t\t\t\t.header(Header.AUTHORIZATION, \"Bearer \" + config.getApiKey())\n\t\t\t\t.body(paramJson)\n\t\t\t\t.timeout(config.getTimeout());","sourceCodeStart":52,"sourceCodeEnd":88,"githubUrl":"https://github.com/chinabugotech/hutool/blob/8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442/hutool-ai/src/main/java/cn/hutool/ai/core/BaseAIService.java#L52-L88","documentation":"Thrown by BaseAIService.sendGet when the underlying HttpRequest.execute() raises a transport-level exception. Hutool-http does NOT throw on HTTP 4xx/5xx status (those come back as a normal HttpResponse with a status code); this exception is only for connection failures: unknown host, connection refused, socket timeout, malformed URL, SSL handshake failure, or proxy errors. The original exception is preserved as the cause.","triggerScenarios":"Wrong/unreachable config.getApiUrl() (DNS resolution failure, connection refused), connect/read timeout exceeded, invalid API URL format, SSL/TLS handshake error, or a configured Proxy that cannot be reached. Affects models that inherit BaseAIService.sendGet without overriding it (deepseek, openai, doubao, grok, hutool).","commonSituations":"Forgot to setApiUrl so the default is wrong; corporate proxy or firewall blocking outbound HTTPS; transient network outage; self-signed endpoint without trust config; timeout too small for slow models; apiKey wrong does NOT trigger this (that returns an HTTP error status, not an exception).","solutions":["Verify config.getApiUrl() is reachable from the host (curl it).","Increase setTimeout / setReadTimeout for slow endpoints.","If behind a proxy, set it via setProxy and confirm it is reachable.","Inspect ex.getCause() for the real IOException/UnknownHostException/SocketTimeoutException.","Note HTTP 401/429/500 are NOT this error -- check HttpResponse.getStatus() separately on success path."],"exampleFix":"// before\nAIConfig cfg = new AIConfigBuilder(ModelName.OPENAI.getValue())\n    .setApiKey(k).build(); // apiUrl default may be unreachable\nString r = AIUtil.chat(cfg, \"hi\"); // -> Failed to send GET request\n\n// after\nAIConfig cfg = new AIConfigBuilder(ModelName.OPENAI.getValue())\n    .setApiKey(k)\n    .setApiUrl(\"https://api.openai.com\")\n    .setTimeout(60_000)\n    .build();","handlingStrategy":"try-catch","validationCode":"// Pre-flight reachability of apiUrl\nString url = config.getApiUrl();\nif (StrUtil.isBlank(url)) throw new IllegalStateException(\"apiUrl not set\");\ntry {\n    new java.net.URL(url).toURI(); // malformed URL early\n} catch (Exception ex) {\n    throw new IllegalStateException(\"bad apiUrl \" + url, ex);\n}","typeGuard":null,"tryCatchPattern":"try {\n    return service.chat(prompt);\n} catch (AIException e) {\n    if (e.getMessage().startsWith(\"Failed to send GET request\")) {\n        Throwable root = e.getCause();\n        // root: UnknownHostException / SocketTimeoutException / ConnectException\n        // -> fix apiUrl / network / timeout; this is NOT an HTTP status error\n    }\n    throw e;\n}","preventionTips":["Always set an explicit apiUrl; do not rely on defaults in production.","Configure realistic connect/read timeouts.","Distinguish transport errors (thrown) from HTTP status errors (returned)."],"tags":["network","http","transport","timeout"],"backgroundTag":null,"analyzedSha":"8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442","analyzedAt":"2026-08-14T04:01:12.892Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}