chinabugotech/hutool · error · AIException

Failed to send GET request:

Error message

Failed to send GET request: 

What it means

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.

Solutions

  1. Verify config.getApiUrl() is reachable from the host (curl it).
  2. Increase setTimeout / setReadTimeout for slow endpoints.
  3. If behind a proxy, set it via setProxy and confirm it is reachable.
  4. Inspect ex.getCause() for the real IOException/UnknownHostException/SocketTimeoutException.
  5. Note HTTP 401/429/500 are NOT this error -- check HttpResponse.getStatus() separately on success path.

Example fix

// before
AIConfig cfg = new AIConfigBuilder(ModelName.OPENAI.getValue())
    .setApiKey(k).build(); // apiUrl default may be unreachable
String r = AIUtil.chat(cfg, "hi"); // -> Failed to send GET request

// after
AIConfig cfg = new AIConfigBuilder(ModelName.OPENAI.getValue())
    .setApiKey(k)
    .setApiUrl("https://api.openai.com")
    .setTimeout(60_000)
    .build();
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight reachability of apiUrl
String url = config.getApiUrl();
if (StrUtil.isBlank(url)) throw new IllegalStateException("apiUrl not set");
try {
    new java.net.URL(url).toURI(); // malformed URL early
} catch (Exception ex) {
    throw new IllegalStateException("bad apiUrl " + url, ex);
}

Try / catch

try {
    return service.chat(prompt);
} catch (AIException e) {
    if (e.getMessage().startsWith("Failed to send GET request")) {
        Throwable root = e.getCause();
        // root: UnknownHostException / SocketTimeoutException / ConnectException
        // -> fix apiUrl / network / timeout; this is NOT an HTTP status error
    }
    throw e;
}

Prevention

When it happens

Trigger: 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).

Common situations: 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).

Related errors


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/a68aac1250192366. Report an issue: GitHub.

Appendix: source

Thrown at hutool-ai/src/main/java/cn/hutool/ai/core/BaseAIService.java:70

	/**
	 * 发送Get请求
	 * @param endpoint 请求节点
	 * @return 请求响应
	 */
	protected HttpResponse sendGet(final String endpoint) {
		//链式构建请求
		try {
			//设置超时3分钟
			HttpRequest httpRequest = HttpRequest.get(config.getApiUrl() + endpoint)
				.header(Header.ACCEPT, "application/json")
				.header(Header.AUTHORIZATION, "Bearer " + config.getApiKey())
				.timeout(config.getTimeout());
			if (config.getHasProxy()) {
				httpRequest.setProxy(config.getProxy());
			}
			return httpRequest.execute();
		} catch (final Exception e) {
			throw new AIException("Failed to send GET request: " + e.getMessage(), e);
		}
	}

	/**
	 * 发送Post请求
	 * @param endpoint 请求节点
	 * @param paramJson 请求参数json
	 * @return 请求响应
	 */
	protected HttpResponse sendPost(final String endpoint, final String paramJson) {
		//链式构建请求
		try {
			HttpRequest httpRequest = HttpRequest.post(config.getApiUrl() + endpoint)
				.header(Header.CONTENT_TYPE, "application/json")
				.header(Header.ACCEPT, "application/json")
				.header(Header.AUTHORIZATION, "Bearer " + config.getApiKey())
				.body(paramJson)
				.timeout(config.getTimeout());

View on GitHub (pinned to 8870454b2a)