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

  1. Download media immediately after the video/audio generation job completes — do not store the URL for later retrieval.
  2. If the URL has expired, re-submit the generation job to get a fresh URL.
  3. Cache the downloaded bytes (Conductor's checkVideoStatus already downloads and stores bytes), so repeated access doesn't re-fetch from the stale URL.
  4. 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

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


AI-assisted analysis of conductor-oss/conductor@cf7c3e4a8a (2026-08-14). Data as JSON: /api/errors/1da26211112eb875. Report an issue: GitHub.