conductor-oss/conductor · error · RuntimeException

Empty response downloading image from {url}

Error message

Empty response downloading image from {url}

What it means

GeminiVideoModel.downloadFromUrl() (private, used for image-to-video input) throws this when the OkHttp response body is null after downloading the input image. This means the image URL returned a response with no content. RuntimeException is then rethrown unchanged by the second catch. This is distinct from error 192/193 which are in GeminiVertex's downloadFromUrl for output media — this one downloads the INPUT image for image-to-video generation.

Source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/providers/gemini/GeminiVideoModel.java:174

                return new VideoResponse(generations, metadata);

            } else {
                metadata.setStatus("PROCESSING");
                log.debug("Gemini Veo video in progress: operation={}", jobId);
                return new VideoResponse(List.of(), metadata);
            }

        } catch (Exception e) {
            log.error("Failed to check Gemini Veo video status for operation {}", jobId, e);
            throw new RuntimeException("Failed to check video status: " + e.getMessage(), e);
        }
    }

    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 image from " + url);
            }
            return response.body().bytes();
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception e) {
            throw new RuntimeException("Failed to download image from " + url, e);
        }
    }
}

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Verify the input image URL is publicly accessible and returns image content — test with curl or a browser first.
  2. Use inline base64 (data: URI or raw base64) instead of a URL to avoid network download entirely — the code supports data:image/...;base64,... format.
  3. If using a pre-signed URL, ensure it has a long enough TTL to survive until the video job is submitted.
  4. Download the image to local storage and pass as base64 to avoid URL expiry issues.

Example fix

// before — URL may expire or be unreachable
VideoOptions opts = VideoOptionsBuilder.builder()
    .model("veo-3.0-generate-001")
    .inputImage("https://temp-bucket.example.com/presigned-image.png")
    .build();

// after — inline base64 avoids network download
String base64Img = Base64.getEncoder().encodeToString(imageBytes);
VideoOptions opts = VideoOptionsBuilder.builder()
    .model("veo-3.0-generate-001")
    .inputImage("data:image/png;base64," + base64Img)
    .build();
Defensive patterns

Strategy: validation

Validate before calling

// Validate input image URL accessibility before passing to video model
void validateInputImageUrl(String url) {
    if (url == null || url.isBlank()) return; // optional field
    if (url.startsWith("http://") || url.startsWith("https://")) {
        try (var resp = new OkHttpClient().newCall(
                new Request.Builder().url(url).head().build()).execute()) {
            if (resp.body() == null) {
                throw new IllegalArgumentException(
                    "Input image URL returns no body: " + url);
            }
        } catch (java.io.IOException e) {
            throw new IllegalArgumentException(
                "Cannot access input image URL: " + url, e);
        }
    }
}

Try / catch

try {
    return videoModel.call(videoPrompt);
} catch (RuntimeException e) {
    if (e.getMessage().startsWith("Empty response downloading image")) {
        throw new IllegalArgumentException(
            "Input image URL returned empty response. "
            + "Use a stable URL or inline base64 (data:image/...;base64,...).", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: An input image URL provided in VideoOptions.getInputImage() (an http/https URL) returns an HTTP response with a null body. The URL may be a pre-signed/expiring link, a broken link, or a server that responds with headers but no body.

Common situations: Using a temporary upload URL (e.g. a pre-signed S3/GCS URL) that has expired by the time the video job is submitted. The image link is broken (404 returning no body). CDN returns an empty response. The image host rate-limits and returns an empty body.

Related errors


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