Timeline segment_overlap: set start times when clips cross-fade

Sume Timeline returns segment_overlap when a slot starts before the previous one ends minus the fade. The rule, the fix, and a Python check.

4 min readSume
All posts

Timeline 1.0 answers 400 segment_overlap when video[i].start is earlier than the previous slot's start + duration minus the length of slot i's own transition. Fix it by moving the start later, or by lengthening the fade. If every start is simply the sum of the durations before it, the error cannot happen, with or without fades.

The related code invalid_segment_timing fires in two cases: a start that is not strictly greater than the previous start, or a slot that runs more than 0.5 s past audio.duration_seconds. Both are caught at submit, so you pay nothing for the mistake.

What exactly does segment_overlap compare?

In the Timeline 1.0 reference the refusal is described as "overlap past the xfade". The API check (apps/api/src/schemas.ts and apps/api/src/timeline.ts) is one inequality per slot after the first: start >= previous.start + previous.duration - transition.duration, where the transition duration is 0 for a hard cut. A 0.25 s fade therefore lets a slot start up to 0.25 s before the previous one ends, and not a frame earlier.

Every slot declares an on-spine start, and the docs say those starts are authoritative: the compiler compensates for the cross-fade and never pre-shifts a slot. That is the practical difference from raw FFmpeg, where you work out an offset yourself.

Timeline 1.0 start-time refusals, from the Timeline 1.0 page and API source, read 2026-10-03
CodeWhen it firesFix
segment_overlapstart < previous start + previous duration - this slot's transition durationMove the start later, or lengthen the fade
invalid_segment_timingstart is not greater than the previous startMake starts strictly increase
invalid_segment_timinga slot ends more than 0.5 s after audio.duration_secondsShorten the slot or lengthen the spine
timeline_must_start_at_zerovideo[0].start is not 0Set the first start to 0

How does that compare with FFmpeg's xfade offset?

The FFmpeg filters documentation defines xfade's offset as the cross-fade start relative to the first input, in seconds, with a default of 0 and a duration range of 0 to 60 seconds. It also requires both inputs to share resolution, pixel format, frame rate and timebase. A chain of N clips means N-1 offsets that you derive from every earlier duration and fade.

On Sume you write the start where the clip belongs on the audio spine, and the six transition types (fade, wipeleft, wiperight, slideup, slidedown, dissolve) are applied for you. Mixed sizes and frame rates are handled by fit and output, not by you.

Can I check starts before sending a job?

Yes. POST /v1/timeline-1.0/plan runs the same schema and compiler checks without creating a job, reserving credits or downloading media, and it needs no Idempotency-Key. It returns duration_seconds, segment_count, billable_minutes and an estimate. A plan cannot predict warnings about short sources that get padded or looped, so keep reading warnings[] on the finished render.

The Python below mirrors the two start rules, so you can lint a video[] array in a unit test. It runs as written with the standard library.

def first_error(video):
    for i in range(1, len(video)):
        prev, cur = video[i - 1], video[i]
        d = cur.get("transition", {}).get("duration", 0)
        if not cur["start"] > prev["start"]:
            return i, "invalid_segment_timing"
        if cur["start"] + 1e-9 < prev["start"] + prev["duration"] - d:
            return i, "segment_overlap"
    return None

slots = [
    {"start": 0, "duration": 8},
    {"start": 7.5, "duration": 8, "transition": {"type": "fade", "duration": 0.25}},
]
print(first_error(slots))        # (1, 'segment_overlap')
slots[1]["start"] = 7.75
print(first_error(slots))        # None

What should I use as starts in the first place?

Accumulate the on-screen durations: slot 0 starts at 0, slot 1 at the sum of slot 0's duration, and so on. That satisfies the overlap rule for any transition length. Set audio.duration_seconds to that total, since the last slot may trail the spine by at most 0.5 s. Submit with Idempotency-Key, poll the job as described in Jobs and results, and read video_url from the result. Timeline renders are $0.10 per ceil(output minute) according to the Timeline page, which also says to confirm the live rate in GET /v1/catalog.

What should I log when it fails?

Log the slot index, the start you sent, the previous slot's start and duration, and the transition duration on the slot. The refusal names the offending slot, so the arithmetic is one subtraction away. Teams that generate timelines from a script usually find the same bug: the start was computed from the source file length instead of the slot's duration, so a clip trimmed with source_in still reserved its full length on the spine.

Keep one builder function that owns both fields. If duration is the only number you pass in, and start is always derived, the overlap refusal turns into a unit test you will never see fail in production, and the unbilled plan call becomes a last line of defence rather than a debugging tool.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume