{"record":{"id":"6cc38eceb0d0c33e","repo":"conductor-oss/conductor","slug":"empty-response-downloading-image-from-url","errorCode":null,"errorMessage":"Empty response downloading image from {url}","messagePattern":"Empty response downloading image from (.+?)","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/providers/gemini/GeminiVideoModel.java","lineNumber":174,"sourceCode":"                return new VideoResponse(generations, metadata);\n\n            } else {\n                metadata.setStatus(\"PROCESSING\");\n                log.debug(\"Gemini Veo video in progress: operation={}\", jobId);\n                return new VideoResponse(List.of(), metadata);\n            }\n\n        } catch (Exception e) {\n            log.error(\"Failed to check Gemini Veo video status for operation {}\", jobId, e);\n            throw new RuntimeException(\"Failed to check video status: \" + e.getMessage(), e);\n        }\n    }\n\n    private byte[] downloadFromUrl(String url) {\n        okhttp3.Request request = new okhttp3.Request.Builder().url(url).get().build();\n        try (okhttp3.Response response = httpClient.newCall(request).execute()) {\n            if (response.body() == null) {\n                throw new RuntimeException(\"Empty response downloading image from \" + url);\n            }\n            return response.body().bytes();\n        } catch (RuntimeException e) {\n            throw e;\n        } catch (Exception e) {\n            throw new RuntimeException(\"Failed to download image from \" + url, e);\n        }\n    }\n}\n","sourceCodeStart":156,"sourceCodeEnd":184,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/providers/gemini/GeminiVideoModel.java#L156-L184","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Verify the input image URL is publicly accessible and returns image content — test with curl or a browser first.","Use inline base64 (data: URI or raw base64) instead of a URL to avoid network download entirely — the code supports data:image/...;base64,... format.","If using a pre-signed URL, ensure it has a long enough TTL to survive until the video job is submitted.","Download the image to local storage and pass as base64 to avoid URL expiry issues."],"exampleFix":"// before — URL may expire or be unreachable\nVideoOptions opts = VideoOptionsBuilder.builder()\n    .model(\"veo-3.0-generate-001\")\n    .inputImage(\"https://temp-bucket.example.com/presigned-image.png\")\n    .build();\n\n// after — inline base64 avoids network download\nString base64Img = Base64.getEncoder().encodeToString(imageBytes);\nVideoOptions opts = VideoOptionsBuilder.builder()\n    .model(\"veo-3.0-generate-001\")\n    .inputImage(\"data:image/png;base64,\" + base64Img)\n    .build();","handlingStrategy":"validation","validationCode":"// Validate input image URL accessibility before passing to video model\nvoid validateInputImageUrl(String url) {\n    if (url == null || url.isBlank()) return; // optional field\n    if (url.startsWith(\"http://\") || url.startsWith(\"https://\")) {\n        try (var resp = new OkHttpClient().newCall(\n                new Request.Builder().url(url).head().build()).execute()) {\n            if (resp.body() == null) {\n                throw new IllegalArgumentException(\n                    \"Input image URL returns no body: \" + url);\n            }\n        } catch (java.io.IOException e) {\n            throw new IllegalArgumentException(\n                \"Cannot access input image URL: \" + url, e);\n        }\n    }\n}","typeGuard":null,"tryCatchPattern":"try {\n    return videoModel.call(videoPrompt);\n} catch (RuntimeException e) {\n    if (e.getMessage().startsWith(\"Empty response downloading image\")) {\n        throw new IllegalArgumentException(\n            \"Input image URL returned empty response. \"\n            + \"Use a stable URL or inline base64 (data:image/...;base64,...).\", e);\n    }\n    throw e;\n}","preventionTips":["Use inline base64 (data:image/png;base64,...) for input images to avoid URL expiry and download failures entirely.","If using a URL, verify it's publicly accessible and returns image content before passing it.","Avoid pre-signed URLs with short TTLs for image-to-video input.","Host input images on a reliable CDN with long-lived URLs."],"tags":["gemini","video","download","empty-response","image-to-video"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}