chinabugotech/hutool · error · AIException

TTS处理失败:

Error message

TTS处理失败: 

What it means

The outer catch-all in HutoolServiceImpl.tts: any Exception (including the inner 'TTS请求失败' throw from error 15, plus any transport failure from sendPost) is re-wrapped as AIException("TTS处理失败: " + e.getMessage(), e). The cause is preserved. When the inner check threw, the visible message is the doubled form 'TTS处理失败: TTS请求失败: <json body>'.

Source

Thrown at hutool-ai/src/main/java/cn/hutool/ai/model/hutool/HutoolServiceImpl.java:129

	}

	@Override
	public InputStream tts(String input, final HutoolCommon.HutoolSpeech voice) {
		try {
			String paramJson = buildTTSRequestBody(input, voice.getVoice());
			final HttpResponse response = sendPost(TTS, paramJson);

			// 检查响应内容类型
			String contentType = response.header("Content-Type");
			if (contentType != null && contentType.startsWith("application/json")) {
				// 如果是JSON响应,说明有错误
				String errorBody = response.body();
				throw new AIException("TTS请求失败: " + errorBody);
			}
			// 默认返回音频流
			return response.bodyStream();
		} catch (Exception e) {
			throw new AIException("TTS处理失败: " + e.getMessage(), e);
		}
	}

	@Override
	public String stt(final File file) {
		final Map<String, Object> paramMap = buildSTTRequestBody(file);
		final HttpResponse response = sendFormData(STT, paramMap);
		return response.body();
	}


	@Override
	public String videoTasks(String text, String image, final List<HutoolCommon.HutoolVideo> videoParams) {
		String paramJson = buildGenerationsTasksRequestBody(text, image, videoParams);
		final HttpResponse response = sendPost(CREATE_VIDEO, paramJson);
		return response.body();
	}

View on GitHub (pinned to 8870454b2a)

Solutions

  1. Catch AIException around tts() and inspect getMessage() (strip the 'TTS处理失败: ' / 'TTS请求失败: ' prefixes) and getCause().
  2. If getCause() is a transport exception, fix network/timeout via config setTimeout/setReadTimeout and setApiUrl.
  3. If the message contains a JSON body, parse it for the upstream error code.
  4. Retry with backoff for transient transport errors.

Example fix

// before
InputStream audio = service.tts(text, voice); // any failure -> TTS处理失败: ...

// after
try {
    return service.tts(text, voice);
} catch (AIException e) {
    Throwable root = e.getCause() != null ? e.getCause() : e;
    log.error("tts failed: {} | root: {}", e.getMessage(), root.toString());
    throw e;
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Pre-flight network + key
if (StrUtil.isBlank(config.getApiKey())) throw new IllegalStateException("key missing");
if (StrUtil.isBlank(config.getApiUrl())) throw new IllegalStateException("apiUrl missing");
if (config.getReadTimeout() < 30_000) config.setReadTimeout(60_000);

Try / catch

try {
    return service.tts(text, voice);
} catch (AIException e) {
    String m = e.getMessage();
    Throwable root = e.getCause();
    if (m.startsWith("TTS处理失败")) {
        if (m.contains("TTS请求失败")) {
            // upstream JSON error body; parse it
            String body = m.substring(m.lastIndexOf("TTS请求失败:") + "TTS请求失败:".length()).trim();
        } else {
            // transport failure -> root is an IOException
        }
    }
    throw e;
}

Prevention

When it happens

Trigger: Any failure during tts(): the upstream returned a JSON error (inner throw), or sendPost raised a transport exception (network/timeout/SSL), or bodyStream() failed. This is the single exception callers of tts() actually catch.

Common situations: Network/timeout talking to the hutool TTS endpoint; invalid key returning a JSON error; unsupported voice; transient upstream failure.

Related errors


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