affaan-m/ECC · error · ValueError
clip ( ) has non-positive duration
Error message
clip {i} ({path}) has non-positive duration {dur!r} What it means
normalise_clips requires every clip to have a strictly positive duration; duration defaults to 0.0 when missing. If clip i's duration is missing, zero, negative, or non-numeric-coercible to <= 0, it raises ValueError naming the clip index, path, and offending duration. Zero/negative durations would produce empty or invalid timeline events.
Solutions
- Ensure each clip has a positive numeric duration in seconds before export
- Convert timecode strings (HH:MM:SS) to seconds before passing clips
- Pre-validate and filter clips: `float(c.get("duration") or 0) > 0`
- Fix upstream arithmetic producing negative durations (e.g. end < start)
Example fix
// before
clips = [{"path": "/media/a.mov", "duration": 0}]
// after
clips = [{"path": "/media/a.mov", "duration": 5.0}] Defensive patterns
Strategy: validation
Validate before calling
def clips_have_positive_durations(clips: list[dict]) -> list[int]:
bad = []
for i, c in enumerate(clips):
try:
if float(c.get("duration") or 0.0) <= 0:
bad.append(i)
except (TypeError, ValueError):
bad.append(i)
return bad Type guard
def has_positive_duration(clip) -> bool:
try:
return float(clip.get("duration") or 0.0) > 0
except (TypeError, ValueError):
return False Try / catch
try:
edl = build_edl(clips, fps=24)
except ValueError as e:
if "non-positive duration" in str(e):
print(e) # names clip index, path, and bad duration
raise Prevention
- Store durations as seconds (floats), never timecode strings
- Sanity-check that computed durations (end - start) are positive before export
- Pre-filter clip lists with a duration validator and log rejected entries
When it happens
Trigger: Calling build_fcpxml/build_edl with a clip lacking 'duration' (coerced to 0.0), duration=0, a negative duration, or a string like "abc" that makes float() fail-adjacent logic produce invalid values — e.g. frames computed from a bad duration.
Common situations: CSV/JSON imports with blank duration cells, durations expressed as "00:00:05" timecodes instead of seconds, unit mix-ups (milliseconds fed as seconds can pass but produce wrong frames; negatives indicate bad subtraction upstream).
Understand the failure class
Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.
Related errors
- clip has no 'path
- clip ( ) has non-positive duration
- clip ( ) has non-positive duration
- genre has invalid total duration
- receipt reference has an invalid finite source duration
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/2df3fc0959bc7b5d.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/export.py:57
# ---------------------------------------------------------------------------
# clip normalisation
# ---------------------------------------------------------------------------
def normalise_clips(clips: Iterable[dict], fps: float | Fraction) -> list[dict]:
"""Validate clips and pre-compute integer frame counts and offsets.
Each clip is ``{"path": str, "duration": float, "name": str?}``.
"""
out: list[dict] = []
offset = 0
for i, c in enumerate(clips):
path = str(c.get("path") or "")
if not path:
raise ValueError(f"clip {i} has no 'path'")
dur = float(c.get("duration") or 0.0)
if dur <= 0:
raise ValueError(f"clip {i} ({path}) has non-positive duration {dur!r}")
frames = max(1, seconds_to_frames(dur, fps)) # never a zero-length event
name = str(c.get("name") or Path(path).stem)
out.append(
{
"path": path,
"name": name,
"frames": frames,
"offset_frames": offset,
"seconds": dur,
}
)
offset += frames
if not out:
raise ValueError("no clips to write - a timeline needs at least one event")
return out
def _file_uri(path: str) -> str:View on GitHub (pinned to 8321021c54)