compose_duration_clamped_to_source: the banner clip came out shorter
Compose clamps video.duration to the source file and warns compose_duration_clamped_to_source. The job still succeeds; read the result length first.

compose_duration_clamped_to_source is a warning, not a failure. It means you asked Timeline compose for a video.duration longer than the file has left, so Sume clamped it to the end of the source and the job still succeeded. The output is shorter than you asked for, and the number to trust is the duration_seconds in the result.
Why it happens
Compose takes the output length from the video layer only: video.duration, or else the rest of the file from video.source_in. The still never changes the length. The ceiling is 300 seconds. So a 10-second request on a 6-second clip gives a 6-second MP4 and the warning, and a source_in of 4 on the same clip leaves 2 seconds.
In a Black Friday workflow this shows up when a banner is composed over a short generated clip and then placed into a longer timeline slot. The timeline slot says 10 seconds, the composed shot is 6, and the rest of the slot is a problem.
| You send | Output length | Warning |
|---|---|---|
| video.duration within the file | As requested | none |
| video.duration past the end of the file | Rest of the file | compose_duration_clamped_to_source |
| No duration, with source_in | Rest of the file after source_in | none |
| Any request | Never more than 300 s | none |
Read the result, then plan the slot
The job result for compose returns video_url and duration_seconds. Use that number, not your request, when you declare the next Timeline slot. The script below submits a compose with an over-long duration, then prints the real length and the warnings. It is a $0.02 flat job.
Whether the warning object is a string or an object in your response, print it whole and match on the code text.
import json, os, time, urllib.request
def call(path, body=None, key=None):
h = {"Authorization": "Bearer " + os.environ["SUME_API_KEY"], "Content-Type": "application/json"}
if key:
h["Idempotency-Key"] = key
data = json.dumps(body).encode() if body else None
return json.load(urllib.request.urlopen(urllib.request.Request("https://api.sume.com" + path, data, h)))
def run(path, body, key):
jid = call(path, body, key)["request_id"]
while call(f"/v1/jobs/{jid}/status")["status"] not in ("completed", "failed", "canceled"):
time.sleep(5)
return call(f"/v1/jobs/{jid}/result")
M = "https://media.sume.com/artifacts/artf_demo/"
out = run("/v1/timeline-1.0/compose", {"operation": "overlay",
"image": {"url": M + "sale-banner.png"},
"video": {"url": M + "six-second-clip.mp4", "duration": 10}}, "banner-v1")
print(out["duration_seconds"], out.get("warnings"))
# Use out["duration_seconds"] for the Timeline slot, not the 10 you asked for.What Timeline does with a short source
If you put a 6-second shot into a 10-second slot anyway, Timeline 1.0 does not fail. The docs say soft warnings include padded or looped short sources, and that a plan cannot predict those warnings. So the render can succeed with a held or looped tail that you did not intend, which on a sale clip means the price card sits there too long or repeats.
Fixes
- Set the slot
durationto the composedduration_seconds. - Or generate or trim the source to at least the length you need before composing.
- Or lengthen the shot by adding a second slot rather than asking compose for more than the file has.
- Treat any compose warning as a review item in your build, not a silent pass.
Limits
Clamping only happens on the duration. A still that is not a still, or a file that is not a video, fails with its own codes (compose_image_not_still, compose_video_not_video).
Sources
Related posts
More in Developers
- Conversational video edits on Omni: a three-turn chain on Sume
Edit a clip in turns on Sume: each Omni video_to_video call takes the last result as video_url. Three turns on a 10 s clip cost $3.75 at 720p. curl chain.
- createSumeClient custom fetch: log ratelimit-remaining as you go
Pass your own fetch to createSumeClient to warn when ratelimit-remaining runs low, without wrapping every SDK call. Works on Node 18+, Bun, Deno, Workers.
- C# HttpClient and Sume images: keep the key off the download call
Reuse one HttpClient, but set the bearer header per request, not as a default header, or it also rides along when you download the Sume image URL.
- C# HttpClient for Sume video jobs: a Sora port in 30 lines
A .NET top-level program that posts to Sume /v1/videos with an Idempotency-Key, polls to a terminal status, and saves the mp4. For C# teams that wrapped Sora.
Written by Sume