Shopify video sources are empty until READY: poll before attaching

On Shopify's Video object, sources and duration stay empty until status is READY. Poll the file before you attach a Sume clip.

5 min readSume
All posts

A Shopify video is not usable the moment fileCreate returns. On the Admin GraphQL Video object, sources is empty and duration is null unless the video's status is READY, so a pipeline that uploads a Sume clip and immediately reads its playback URLs gets nothing. Poll until the file reports READY, and keep that wait separate from your Sume job polling.

Shopify details come from the Video object, fileCreate and fileUpdate references, read 2026-10-02. Sume details come from Jobs and results.

What exactly is empty before READY?

The Video object lists sources (the playable renditions), preview (a preview image), duration in milliseconds, filename and status. Shopify's text says sources is empty and duration is null until status is READY. The page does not list which formats or resolutions Shopify produces, so do not hard-code a rendition list from another source.

fileCreate says the same thing from the file side: files process asynchronously, fileStatus tracks that, and READY means processing succeeded.

Which steps need READY?

Reading playable sources and duration needs it. So does editing: fileUpdate requires the file to be in a ready state, and the mutation locks the file during updates. Shopify's page also says videos and 3D models allow only alt text and product-reference updates, and you cannot change originalSource and previewImageSource in one call, so a video poster swap is a separate step. See the poster post for that.

Steps and their readiness needs, from Shopify's references read 2026-10-02
StepNeeds READYSource
Read sources or durationYes, empty or null beforeVideo object
fileUpdate on the fileYes, file must be readyfileUpdate
Show the statusNo, fileStatus is readable at any timefileCreate
Attach to a productNot stated on the pages readTest on a draft product

How do I poll without re-uploading?

Back off between checks and give up with an error that says what to do next. The loop below reads fileStatus and duration through the generic node query and doubles the delay up to 30 seconds. The important line is the last one: a timeout is a reason to look again later, not a reason to create the file a second time and pay for another Sume render.

import os, time, requests

GQL = (f"https://{os.environ['SHOP']}/admin/api/"
       f"{os.environ['SHOPIFY_API_VERSION']}/graphql.json")
Q = """query($id: ID!) {
  node(id: $id) {
    ... on File { fileStatus }
    ... on Video { duration }
  }
}"""

def wait_ready(file_id, tries=8):
    delay = 2
    for _ in range(tries):
        r = requests.post(GQL, json={"query": Q, "variables": {"id": file_id}},
                          headers={"X-Shopify-Access-Token": os.environ["SHOPIFY_TOKEN"]},
                          timeout=30)
        r.raise_for_status()
        node = r.json()["data"]["node"]
        if node["fileStatus"] == "READY":
            return node.get("duration")  # milliseconds, null until READY
        time.sleep(delay)
        delay = min(delay * 2, 30)
    raise TimeoutError(f"{file_id} not READY; do not re-upload, check it again")

What if the file never becomes READY?

Shopify's pages say nothing about how long processing takes or which failure statuses exist besides READY, so do not hard-code a time limit from guesswork. Pick a ceiling that suits your pipeline, such as eight checks with growing delays as in the sketch, then stop and surface the file id for a person to look at.

Do not create the file again as a retry. That leaves an orphan on the Files page and, if the upload came from a Sume render you paid for, wastes nothing on the Sume side but time. If the video is truly stuck, regenerate only after you know the cause, and use a new Idempotency-Key for the new render. Generation admission describes how queued and processing jobs count against your accepted-job capacity, so a stuck pipeline that keeps submitting can also fill your queue.

Is this the same as polling Sume jobs?

The pattern is the same, the systems are not. Sume job statuses are queued, processing, completed, failed and canceled; the last three are terminal, and the docs tell you to use exponential backoff and not to resubmit a paid request because a local process timed out. Shopify's gate is READY. Run both waits in order: Sume job to completed, upload, then Shopify file to READY.

Store both ids. When a run dies half-way, the Sume job id lets you read the finished clip again without resubmitting, and the Shopify file id lets you resume the wait.

Sources

Related posts

More in Integrations

All Integrations posts

Written by Sume