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
- Catch AIException around tts() and inspect getMessage() (strip the 'TTS处理失败: ' / 'TTS请求失败: ' prefixes) and getCause().
- If getCause() is a transport exception, fix network/timeout via config setTimeout/setReadTimeout and setApiUrl.
- If the message contains a JSON body, parse it for the upstream error code.
- 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
- Catch AIException once around tts(); the cause distinguishes JSON-error vs transport.
- Strip the doubled 'TTS处理失败: TTS请求失败: ' prefix to reach the body.
- Log getCause() to separate network issues from upstream rejections.
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
- TTS请求失败:
- Upload failed
- Failed to download media from URL:
- Failed to get remote file MIME type
- Failed to create AIConfig instance
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/81af448f38b31677.
Report an issue: GitHub.