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.

4 min readSume
All posts

invalid_transition_type is a 400 from Timeline 1.0 when video[].transition.type is not one of fade, wipeleft, wiperight, slideup, slidedown or dissolve. If you sent xfade, the message says so directly: xfade is the internal ffmpeg filter name, so use fade or another listed value. It is a validation error, retryable only after you change the body.

What the body returns

The error carries next_action: use_supported_transition_type, the field that failed, the value you received and the allowed list, so a client can show the choices to the user.

Accepted transition.type values and nearby rules (Sume Timeline docs, read 2026-10-05)
RuleValue
Allowed typesfade, wipeleft, wiperight, slideup, slidedown, dissolve
Slots that may have oneOnly slots after the first
Maximum duration1 second
Relative limitAt most 50% of the shorter neighbor
MinimumAt least one output frame
Chained fadesMore than 8 adjacent fades is too_many_chained_transitions

Other transition refusals are separate codes

A valid type can still be refused. transition_on_first_segment means video[0] has a transition. transition_too_long and transition_not_frame_aligned compare the duration with the neighbors and the frame rate. too_many_chained_transitions asks for a hard cut. Keep these apart in your error handler, because invalid_transition_type is about the name, while the others are about timing.

Normalize names before you send

If your editor exports ffmpeg names, translate them once at the edge:

ALLOWED = {"fade", "wipeleft", "wiperight", "slideup", "slidedown", "dissolve"}
ALIASES = {"xfade": "fade", "crossfade": "fade"}

def transition_type(name: str) -> str:
    name = ALIASES.get(name.lower(), name.lower())
    if name not in ALLOWED:
        raise ValueError(f"{name!r} not in {sorted(ALLOWED)}")
    return name

print(transition_type("xfade"), transition_type("WipeLeft"))

Why the allowed list is short

The public type is a small, stable set on purpose. xfade is one filter inside the worker's ffmpeg graph, and the docs keep ffmpeg arguments, filtergraphs and codec settings out of the public contract: keys like filtergraph, ffmpeg_args, codec and crf are rejected with a 400. That keeps your integration independent of the renderer's internals, and it lets the renderer change without breaking your calls.

When you need a look that the six types cannot give, cut instead: a hard cut is always valid, and it avoids the chained-fade limit entirely.

Testing your presets

If you ship transition presets to users, test each preset name through the plan endpoint in CI. It takes one request per preset and costs nothing, and it catches a rename before a user does. Keep the alias map in one module, so that a new editor export format only needs one new line.

Check with the plan call

POST /v1/timeline-1.0/plan validates the schema and compiles the program without a job, credits or media download, so a bad transition fails at no cost. The Timeline docs say the compiler compensates for the overlap that a fade creates and never pre-shifts your declared starts, so you do not need to subtract anything yourself.

The full table is on the Timeline page.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume