{"record":{"id":"4ffb52b2d07f8c1c","repo":"conductor-oss/conductor","slug":"failed-to-check-video-status","errorCode":null,"errorMessage":"Failed to check video status: ","messagePattern":"Failed to check video status: ","errorType":"exception","errorClass":"RuntimeException","httpStatus":null,"severity":"error","filePath":"ai/src/main/java/org/conductoross/conductor/ai/providers/openai/OpenAIVideoModel.java","lineNumber":149,"sourceCode":"            } else if (\"failed\".equals(status.status())) {\n                metadata.setStatus(\"FAILED\");\n                metadata.setErrorMessage(\n                        \"OpenAI video generation failed: %s\".formatted(status.toString()));\n                log.error(\"OpenAI Sora video failed: id={}, response = {}\", jobId, status);\n                return new VideoResponse(List.of(), metadata);\n\n            } else {\n                // queued or in_progress\n                metadata.setStatus(\"PROCESSING\");\n                log.debug(\n                        \"OpenAI Sora video in progress: id={}, progress={}%\",\n                        jobId, status.progress());\n                return new VideoResponse(List.of(), metadata);\n            }\n\n        } catch (Exception e) {\n            log.error(\"Failed to check OpenAI video status for job {}\", jobId, e);\n            throw new RuntimeException(\"Failed to check video status: \" + e.getMessage(), e);\n        }\n    }\n\n    /**\n     * Maps OpenAI status strings to our canonical status values.\n     *\n     * <p>OpenAI uses: queued, in_progress, completed, failed\n     */\n    private String mapStatus(String openaiStatus) {\n        return switch (openaiStatus) {\n            case \"completed\" -> \"COMPLETED\";\n            case \"failed\" -> \"FAILED\";\n            default -> \"PROCESSING\";\n        };\n    }\n\n    /**\n     * Resolves an input image specification to raw bytes.","sourceCodeStart":131,"sourceCodeEnd":167,"githubUrl":"https://github.com/conductor-oss/conductor/blob/cf7c3e4a8adfb158be778ab1ec525323c363cd3a/ai/src/main/java/org/conductoross/conductor/ai/providers/openai/OpenAIVideoModel.java#L131-L167","documentation":"OpenAIVideoModel.checkStatus() catches a generic Exception and wraps it in a RuntimeException with message \"Failed to check video status: \". This is the polling path: GET /v1/videos/{jobId}. Any exception during status polling or result download (api.getVideoStatus, api.downloadVideo, api.downloadThumbnail) is caught and rethrown. The catch covers the entire method body including the completed-download branch.","triggerScenarios":"checkVideoStatus() polls a job by jobId. Failure occurs when: the jobId is invalid or expired, the API key was revoked between submission and polling, network timeout downloading the large MP4 video binary, the video content endpoint returns an error, or a transient network blip during polling.","commonSituations":"Polling a job whose ID expired (OpenAI may purge old video records); network timeout downloading a large completed video; thumbnail download fails (though that path has its own try/catch); rate limit on status polling; API key revoked mid-job.","solutions":["Inspect getCause() and jobId in the log — the catch logs \"Failed to check OpenAI video status for job {jobId}\".","For transient network errors during polling, retry checkStatus() with backoff (the Conductor async video workflow already polls).","Verify the jobId is still valid — if the job was submitted long ago, it may have been purged.","If the download itself fails (large MP4), check network bandwidth and OkHttp read timeout configuration."],"exampleFix":"// before: single checkStatus call with no retry\nVideoResponse resp = videoModel.checkStatus(jobId);\n// after: retry with backoff for transient failures\nVideoResponse resp;\nint attempts = 0;\nwhile (true) {\n    try {\n        resp = videoModel.checkStatus(jobId);\n        break;\n    } catch (RuntimeException e) {\n        if (++attempts >= 3) throw e;\n        Thread.sleep(5000L * attempts);\n    }\n}","handlingStrategy":"retry","validationCode":"// Validate jobId before polling\nif (jobId == null || jobId.isBlank()) {\n    throw new IllegalArgumentException(\"Job ID is required to check video status\");\n}\n// Verify jobId format (OpenAI video IDs are typically alphanumeric with hyphens)\nif (!jobId.matches(\"[a-zA-Z0-9_-]+\")) {\n    throw new IllegalArgumentException(\"Invalid job ID format: \" + jobId);\n}","typeGuard":"null","tryCatchPattern":"// checkStatus is inherently a polling operation — retry transient failures\nint maxRetries = 3;\nfor (int attempt = 0; attempt <= maxRetries; attempt++) {\n    try {\n        VideoResponse response = videoModel.checkStatus(jobId);\n        return response;\n    } catch (RuntimeException e) {\n        if (attempt == maxRetries) throw e;\n        Throwable cause = e.getCause();\n        if (cause instanceof IOException || cause instanceof java.net.SocketTimeoutException) {\n            log.warn(\"Transient error polling video job {} (attempt {}), retrying\", jobId, attempt + 1);\n            Thread.sleep(5000L * (attempt + 1));\n            continue;\n        }\n        throw e;\n    }\n}","preventionTips":["Treat checkStatus as a retryable polling operation — transient network errors are expected.","Log the jobId with each poll for traceability.","Set a maximum job lifetime; if polling fails persistently, the job may have expired.","Monitor for video download timeouts (large MP4 files) and adjust OkHttp read timeout."],"tags":["openai","video-generation","sora","polling","network","ai"],"backgroundTag":null,"analyzedSha":"cf7c3e4a8adfb158be778ab1ec525323c363cd3a","analyzedAt":"2026-08-14T03:33:19.897Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}