chinabugotech/hutool · error · AIException

Failed to send POST request:

Error message

Failed to send POST request:

What it means

Thrown by GeminiServiceImpl.sendPost (Gemini-specific override) on a transport-level failure of the POST. Uses x-goog-api-key auth. Hutool-http returns HTTP error statuses as a normal HttpResponse, so 4xx/5xx do NOT raise this -- only connection/socket/URL/SSL/proxy failures do. The message uses a full-width colon (:, U+FF1A). Cause is preserved.

Source

Thrown at hutool-ai/src/main/java/cn/hutool/ai/model/gemini/GeminiServiceImpl.java:494

			throw new AIException("Failed to send GET request: " + e.getMessage(), e);
		}
	}

	@Override
	protected HttpResponse sendPost(String endpoint, String paramJson) {
		//链式构建请求
		try {
			final HttpRequest httpRequest = HttpRequest.post(config.getApiUrl() + endpoint)
				.header(Header.CONTENT_TYPE, "application/json")
				.header("x-goog-api-key", config.getApiKey())
				.body(paramJson)
				.timeout(config.getTimeout());
			if (config.getHasProxy()) {
				httpRequest.setProxy(config.getProxy());
			}
			return httpRequest.execute();
		} catch (final Exception e) {
			throw new AIException("Failed to send POST request:" + e.getMessage(), e);
		}
	}

	/**
	 * 支持流式返回的 POST 请求
	 *
	 * @param endpoint 请求地址
	 * @param paramMap 请求参数
	 * @param callback 流式数据回调函数
	 */
	@Override
	protected void sendPostStream(String endpoint, final Map<String, Object> paramMap, final Consumer<String> callback) {
		HttpURLConnection connection = null;
		try {
			// 创建连接
			URL apiUrl = new URL(config.getApiUrl() + endpoint);
			connection = (HttpURLConnection) apiUrl.openConnection();
			if (config.getHasProxy()) {

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Verify config.getApiUrl() is a reachable Gemini endpoint.
  2. Increase setTimeout/setReadTimeout for slow operations (video, TTS).
  3. Inspect ex.getCause() for the underlying IOException.
  4. When matching this message in logs, use the full-width colon literally.
  5. For business errors (400/403/429), inspect the returned HttpResponse status/body.

Example fix

// before
String r = gemini.predictVideo("long prompt"); // times out
// -> Failed to send POST request:...

// after
AIConfig cfg = new AIConfigBuilder(ModelName.GEMINI.getValue())
    .setApiKey(k)
    .setTimeout(60_000)
    .setReadTimeout(300_000) // video gen is slow
    .build();
Defensive patterns

Strategy: try-catch

Validate before calling

// Generous timeout for slow Gemini POST operations (video/TTS)
if (config.getReadTimeout() < 120_000) config.setReadTimeout(300_000);
if (StrUtil.isBlank(config.getApiKey())) throw new IllegalStateException("key missing");

Try / catch

try {
    return gemini.predictVideo(prompt);
} catch (AIException e) {
    // message uses full-width colon (:)
    if (e.getMessage().contains("Failed to send POST request")) {
        Throwable root = e.getCause(); // transport IOException
        // HTTP 4xx/5xx (incl. invalid key 403) are returned, not thrown here
    }
    throw e;
}

Prevention

When it happens

Trigger: Gemini POST (chat, predictVideo, textToSpeech, upload start) to an unreachable apiUrl, timeout, SSL failure, or proxy failure. A malformed request body or invalid key returns an HTTP status (handled by the caller), not this exception.

Common situations: Wrong Gemini base URL; proxy blocking googleapis.com; long video/TTS generation exceeding timeout; transient outage; regional restriction.

Related errors


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