conductor-oss/conductor · error · IOException
Speech API returned empty body
Error message
Speech API returned empty body
What it means
Thrown by OpenAISpeechApi.createSpeech when the TTS request succeeds (2xx) but OkHttp's response.body() is null. This is a defensive guard rather than a normal OpenAI behavior, since a successful speech response always carries audio bytes.
Source
Thrown at ai/src/main/java/org/conductoross/conductor/ai/providers/openai/api/OpenAISpeechApi.java:86
Request httpRequest =
new Request.Builder()
.url(baseUrl + "/audio/speech")
.header(authHeaderName, authHeaderValue)
.header("Content-Type", "application/json")
.post(RequestBody.create(jsonBody, JSON))
.build();
try (Response response = httpClient.newCall(httpRequest).execute()) {
if (!response.isSuccessful()) {
ResponseBody body = response.body();
String errorBody = body != null ? body.string() : "";
throw new IOException(
"Speech API failed with status %d: %s"
.formatted(response.code(), errorBody));
}
ResponseBody body = response.body();
if (body == null) {
throw new IOException("Speech API returned empty body");
}
return body.bytes();
}
}
@JsonInclude(JsonInclude.Include.NON_NULL)
public record SpeechRequest(
String model,
String input,
String voice,
@JsonProperty("response_format") String responseFormat,
Double speed) {}
}
View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Inspect any OkHttp interceptors configured on the shared httpClient for premature body consumption.
- Bypass intermediate proxies and call OpenAI directly to confirm the body is non-empty.
- If a gateway is required, ensure it streams the binary audio body through unchanged.
Defensive patterns
Strategy: try-catch
Try / catch
try {
byte[] audio = speechApi.createSpeech(req);
} catch (IOException e) {
if (e.getMessage().contains("empty body")) {
// likely a proxy issue; retry once against the direct endpoint
} else {
throw e;
}
} Prevention
- Do not install OkHttp interceptors that consume response bodies on the shared client.
- Test against the direct OpenAI endpoint before routing through corporate proxies.
- Ensure proxies forward binary audio bodies unchanged.
When it happens
Trigger: An intermediary (reverse proxy, WAF, corporate gateway, or load balancer) returns a 200 with no entity body, or OkHttp is configured with a response interceptor that consumes/nulls the body before createSpeech reads it.
Common situations: A misconfigured gateway rewriting the response, a buggy OkHttp interceptor calling body().string() earlier in the chain, or a transparent caching proxy serving an empty 200.
Related errors
- Speech API failed with status %d: %s
- OpenAI Video download returned empty body
- OpenAI Video thumbnail download returned empty body
- Stability AI API returned empty response body
- OpenAI Video API submit failed with status %d: %s
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/5d9664609ecfa801.
Report an issue: GitHub.