conductor-oss/conductor · error · RuntimeException
Empty response downloading from {url}
Error message
Empty response downloading from {url} What it means
GeminiVertex.downloadFromUrl() throws this when the OkHttp response body is null after downloading from a URL returned by the Gemini API for generated media (video, audio). A null body means the server responded but sent no content — typically because the signed URL has expired or the resource is no longer available. RuntimeException is then rethrown unchanged (the second catch block passes it through).
Source
Thrown at ai/src/main/java/org/conductoross/conductor/ai/providers/gemini/GeminiVertex.java:193
.data(Base64.getDecoder().decode(video.getB64Json()))
.mimeType(mimeType)
.build());
} else if (video.getUrl() != null) {
byte[] bytes = downloadFromUrl(video.getUrl());
mediaList.add(Media.builder().data(bytes).mimeType(mimeType).build());
}
}
builder.media(mediaList);
}
return builder.build();
}
private byte[] downloadFromUrl(String url) {
okhttp3.Request request = new okhttp3.Request.Builder().url(url).get().build();
try (okhttp3.Response response = httpClient.newCall(request).execute()) {
if (response.body() == null) {
throw new RuntimeException("Empty response downloading from " + url);
}
return response.body().bytes();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException("Failed to download from " + url, e);
}
}
@Override
public LLMResponse generateAudio(AudioGenRequest request) {
GeminiApi.SpeechConfig speechConfig =
new GeminiApi.SpeechConfig(
new GeminiApi.VoiceConfig(
new GeminiApi.PrebuiltVoiceConfig(request.getVoice())));
GeminiApi.GenerationConfig genConfig =
new GeminiApi.GenerationConfig(
null,View on GitHub (pinned to cf7c3e4a8a)
Solutions
- Download media immediately after the video/audio generation job completes — do not store the URL for later retrieval.
- If the URL has expired, re-submit the generation job to get a fresh URL.
- Cache the downloaded bytes (Conductor's checkVideoStatus already downloads and stores bytes), so repeated access doesn't re-fetch from the stale URL.
- Increase the OkHttp timeout to ensure large media downloads complete before the URL expires.
Example fix
// before — download later, URL may expire
String videoUrl = response.getResults().get(0).getUrl();
// ... store url, download hours later
byte[] bytes = downloadFromUrl(videoUrl); // null body
// after — download immediately when status is COMPLETED
if ("COMPLETED".equals(status)) {
byte[] bytes = downloadFromUrl(video.getUrl());
mediaList.add(Media.builder().data(bytes).mimeType(mime).build());
} Defensive patterns
Strategy: validation
Validate before calling
// Validate the media URL before downloading
void validateMediaUrl(String url) {
if (url == null || url.isBlank()) {
throw new IllegalArgumentException("Media URL is null or empty");
}
// Check if this is likely a signed URL that may have expired
if (url.contains("Expires=")) {
long expires = extractExpiry(url);
if (expires < System.currentTimeMillis() / 1000) {
throw new IllegalStateException(
"Media URL has expired — re-generate to get a fresh URL");
}
}
} Try / catch
try {
byte[] media = vertex.downloadFromUrl(url);
} catch (RuntimeException e) {
if (e.getMessage().startsWith("Empty response downloading from")) {
throw new IllegalStateException(
"Media URL returned empty response — likely expired. "
+ "Re-generate the video/audio to get a fresh URL.", e);
}
throw e;
} Prevention
- Download media immediately after generation completes — do not store URLs for later retrieval.
- Cache the downloaded bytes in Conductor's media storage (checkVideoStatus already does this) so subsequent access doesn't re-fetch.
- If processing old results, re-submit the generation job to get a fresh signed URL rather than reusing a stale one.
- Set generous OkHttp timeouts for large media files.
When it happens
Trigger: The URL returned by Gemini for a generated video or audio resource returns an HTTP response with a null body. This happens when: the signed URL has expired (Google signed URLs have a limited TTL), the resource was garbage-collected after a delay between generation and download, or the CDN returned an empty response.
Common situations: A delay between video generation completion (checkVideoStatus returns COMPLETED) and the actual download of the media. The signed URL TTL expired during processing. The job was from a previous session and the URL is stale. Conductor restarted and is reprocessing old results.
Related errors
- Failed to download from {url}
- Empty response downloading image from {url}
- Gemini generateContent failed: {message}
- No embeddings returned from Gemini API
- Failed to download image from {url}
AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14).
Data as JSON: /api/errors/1da26211112eb875.
Report an issue: GitHub.