Timeline invalid_fit 400: fit must be cover, contain, stretch or blur
invalid_fit means video[].fit is not cover, contain, stretch or blur. Cover is the default, so omit the field if you want it. details.allowed lists values.

invalid_fit is a 400 from Timeline 1.0 when video[].fit is not cover, contain, stretch or blur. The message is "video[].fit must be one of: cover, contain, stretch, blur." and the body has next_action: use_supported_fit, the field path, the value you sent and the allowed list. Words from CSS or ffmpeg, such as fill, scale-down or crop, are not accepted.
Choose a value
cover is the default, so you can leave the field out. The others change what happens when a source's shape differs from the output frame, which is 1080 by 1920 unless you set output.width and output.height.
| fit | Use when |
|---|---|
| cover | Default. Fill the frame; edges may be cropped |
| contain | Keep the whole picture; bars may appear |
| stretch | Force the frame; the picture distorts |
| blur | Keep the whole picture over a blurred fill instead of bars |
Where the error shows up in a real body
The check is on the path video[*].fit, so the field in the error includes the slot index. A 40-slot program with one typo returns that slot, not a generic message, so fix that item and resend. Check every slot on your side in one pass, so that you fix all of them before the next call.
Choosing between the four
Pick by what the viewer should lose. If a cropped edge is acceptable, leave the default. If a product or a face must stay whole, use contain when bars are fine, or blur when the frame should look full. Reach for stretch only for abstract footage, where distortion is invisible. A mixed program can use a different value per slot, which is why the check is per item.
Validate before the call
A tiny local check removes the round trip. This runs as is:
FITS = {"cover", "contain", "stretch", "blur"}
def bad_fits(video: list[dict]) -> list[tuple[int, str]]:
return [(i, s["fit"]) for i, s in enumerate(video)
if "fit" in s and s["fit"] not in FITS]
print(bad_fits([{"fit": "cover"}, {"fit": "fill"}, {}]))What the error does not cover
invalid_fit is about the name of the value. A valid fit can still give a result you dislike, for example contain on a very tall source leaves wide bars. That is a design choice, not an error, and Sume does not warn about it. Preview a still frame on your side if the look matters, and choose the value from the shape of the sources, not from habit.
Free preflight
POST /v1/timeline-1.0/plan returns the same 400 with no job and no credits, and it does not need an Idempotency-Key. Use it in CI on your templates. The field table lives on the Timeline page, and the related posts compare cover, contain and blur by the numbers.
Sources
Related posts
More in Developers
- Timeline invalid_fps 400: output.fps must be 24, 25, 30 or 60
invalid_fps rejects any output.fps outside 24, 25, 30 and 60. Omit the field to match the source frame rate; details.allowed lists the accepted values.
- Timeline invalid_transition_type: why xfade is rejected (use fade)
transition.type accepts fade, wipeleft, wiperight, slideup, slidedown and dissolve. xfade is the internal ffmpeg name, so it returns invalid_transition_type.
- Is Luma Ray3.2 on Sume? Check the video catalog ids in Python
Ray3.2 is not a Sume model. Rather than trust a blog, list GET /v1/video-router/models and test for an id. A short Python script that prints every id.
- Java 21: poll many Sume jobs with virtual threads and a Semaphore
Poll 50 Sume jobs from one Java 21 file using virtual threads, a Semaphore cap of 8, and java.net.http. No Maven, no dependencies, runs with java Poll.java.
Written by Sume