jeecgboot/JeecgBoot · error · RuntimeException

TTS API调用失败,状态码: {statusCode},{errorBody}

Error message

TTS API调用失败,状态码: {statusCode},{errorBody}

What it means

VoiceApiHelper.generateAudio() POSTs an OpenAI-compatible request to {apiHost}/audio/speech. Any HTTP status other than 200 is thrown as a RuntimeException that embeds both the status code and the raw upstream error body. The request uses Bearer apiKey auth with a configurable timeout.

Source

Thrown at jeecg-boot/jeecg-boot-module/jeecg-boot-module-airag/src/main/java/org/jeecg/modules/airag/voice/util/VoiceApiHelper.java:83

        body.put("voice", voice);
        body.put("speed", speed);
        body.put("response_format", "wav");

        log.info("TTS请求: url={}, model={}, voice={}, speed={}, textLength={}", url, config.getModel(), voice, speed, text.length());

        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create(url))
                .header("Authorization", "Bearer " + config.getApiKey())
                .header("Content-Type", "application/json")
                .POST(HttpRequest.BodyPublishers.ofString(body.toJSONString()))
                .timeout(Duration.ofSeconds(config.getTimeout()))
                .build();

        HttpResponse<InputStream> response = httpClient.send(request, HttpResponse.BodyHandlers.ofInputStream());
        if (response.statusCode() != 200) {
            String errorBody = new String(response.body().readAllBytes());
            log.error("TTS API调用失败: status={}, body={}", response.statusCode(), errorBody);
            throw new RuntimeException("TTS API调用失败,状态码: " + response.statusCode() + "," + errorBody);
        }

        try (InputStream is = response.body()) {
            Files.copy(is, audioPath, StandardCopyOption.REPLACE_EXISTING);
        }
        log.info("TTS语音已生成: {}", audioPath);
    }
}

View on GitHub (pinned to 96fb33f5ec)

Solutions

  1. Read the status code and errorBody baked into the message -- they identify the failure class.
  2. 401/403 -> correct/regenerate the apiKey in aiChatConfig.aiModelVoice.
  3. 429 -> add backoff/throttling or raise provider quota; retry with jitter.
  4. 404/400 -> verify apiHost base URL and that model + voice exist on the provider.
  5. Confirm the provider actually exposes the /audio/speech endpoint.
Defensive patterns

Strategy: retry

Validate before calling

// before calling VoiceApiHelper, sanity-check config
AiChatConfig.VoiceModelConfig c = aiChatConfig.getAiModelVoice();
if (oConvertUtils.isEmpty(c.getApiHost()) || oConvertUtils.isEmpty(c.getApiKey())) {
    throw new IllegalStateException("TTS apiHost/apiKey 未配置");
}

Try / catch

// retry only transient (5xx/429) failures; fail fast on 4xx auth errors
try {
    voiceApiHelper.generateAudio(text, audioPath, voice, speed);
} catch (RuntimeException e) {
    String msg = e.getMessage();
    if (msg.contains("状态码: 429") || msg.contains("状态码: 5")) {
        // backoff and retry once
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: 401 = apiKey invalid/revoked; 403/region blocked; 404 = apiHost or model wrong; 429 = rate/quota exceeded; 400 = unsupported voice/model/response_format; 5xx = provider outage. Also a missing/blank apiHost produces a malformed URL.

Common situations: apiKey expired or copied with whitespace; apiHost missing trailing handling mismatch; model name not provisioned on the account; provider rate limiting under load; apiHost pointing at a non-OpenAI base that lacks /audio/speech.

Related errors


AI-assisted analysis of jeecgboot/JeecgBoot@96fb33f5ec (2026-08-14). Data as JSON: /api/errors/0ce5db59d2587a66. Report an issue: GitHub.