conductor-oss/conductor · error · IOException

OpenAI Video download failed with status %d

Error message

OpenAI Video download failed with status %d

What it means

Thrown by OpenAIVideoApi.downloadVideo (the byte[] variant) when GET /v1/videos/{id}/content returns non-2xx. Unlike the streaming variant (224), this message omits the error body and reports only the status code. IOException is thrown inside a try-with-resources so the response is auto-closed.

Source

Thrown at ai/src/main/java/org/conductoross/conductor/ai/providers/openai/api/OpenAIVideoApi.java:199

    }

    /**
     * Download the completed video as a byte array.
     *
     * @param videoId The video job ID
     * @return byte array of the MP4 binary data
     */
    public byte[] downloadVideo(String videoId) throws IOException {
        Request request =
                new Request.Builder()
                        .url(baseUrl + "/v1/videos/" + videoId + "/content")
                        .header("Authorization", "Bearer " + apiKey)
                        .get()
                        .build();

        try (Response response = httpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException(
                        "OpenAI Video download failed with status %d".formatted(response.code()));
            }
            ResponseBody body = response.body();
            if (body == null) {
                throw new IOException("OpenAI Video download returned empty body");
            }
            return body.bytes();
        }
    }

    /**
     * Download the thumbnail for a completed video.
     *
     * @param videoId The video job ID
     * @return byte array of the thumbnail image (webp format)
     */
    public byte[] downloadThumbnail(String videoId) throws IOException {
        Request request =

View on GitHub (pinned to cf7c3e4a8a)

Solutions

  1. Gate the call on getVideoStatus()=='completed' first.
  2. Prefer downloadVideoStream for large videos to avoid loading the whole MP4 into a byte[].
  3. On 404/410 regenerate the video.
  4. If you need the error body for diagnosis, use downloadVideoStream which captures it.
Defensive patterns

Strategy: try-catch

Validate before calling

// Gate on completion before downloading
VideoStatusResponse status = videoApi.getVideoStatus(videoId);
if (!"completed".equalsIgnoreCase(status.status())) {
    throw new IllegalStateException("Video not completed: " + status.status());
}

Try / catch

try {
    byte[] mp4 = videoApi.downloadVideo(videoId);
} catch (IOException e) {
    if (e.getMessage().contains("status 404") || e.getMessage().contains("status 410")) {
        // asset expired - regenerate
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: Downloading before completion (400/409), expired asset (404/410), invalid key (401), or rate limiting (429). Same failure modes as 224 but the byte[] path loses the response body detail for diagnosis.

Common situations: Calling downloadVideo prematurely, asset TTL elapsed, or transient download errors on large MP4s loaded fully into memory.

Related errors


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