{"record":{"id":"f6c0370c5bcafc4f","repo":"affaan-m/ECC","slug":"clip-i-path-has-non-positive-duration-dur-r","errorCode":null,"errorMessage":"clip {i} ({path}) has non-positive duration {dur!r}","messagePattern":"clip (.+?) \\((.+?)\\) has non-positive duration (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"skills/taste-application/scripts/taste/timeline.py","lineNumber":221,"sourceCode":"# ---------------------------------------------------------------------------\n\n\ndef _normalise(clips: Iterable[dict], fps: float | Fraction) -> list[dict]:\n    \"\"\"Validate clips and pre-compute integer frame counts and offsets.\n\n    Returns dicts with ``path``, ``name``, ``frames`` (int, >= 1) and\n    ``offset_frames`` (int). Working in frames from here down is what makes the\n    sequence duration exactly the sum of the clip durations.\n    \"\"\"\n    out: list[dict] = []\n    offset = 0\n    for i, c in enumerate(clips):\n        path = str(c.get(\"path\") or \"\")\n        if not path:\n            raise ValueError(f\"clip {i} has no 'path'\")\n        dur = float(c.get(\"duration\") or 0.0)\n        if dur <= 0:\n            raise ValueError(f\"clip {i} ({path}) has non-positive duration {dur!r}\")\n        frames = max(1, seconds_to_frames(dur, fps))  # never emit a zero-length event\n        name = str(c.get(\"name\") or Path(path).stem)\n        out.append(\n            {\n                \"path\": path,\n                \"name\": name,\n                \"frames\": frames,\n                \"offset_frames\": offset,\n                \"seconds\": dur,\n            }\n        )\n        offset += frames\n    if not out:\n        raise ValueError(\"no clips to write - a timeline needs at least one event\")\n    return out\n\n\ndef _file_uri(path: str) -> str:","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/skills/taste-application/scripts/taste/timeline.py#L203-L239","documentation":"timeline.py raises ValueError('clip {i} ({path}) has non-positive duration {dur!r}') in _normalise when a clip's duration is 0 or negative. Durations must be positive so each event occupies at least one frame in the output timeline.","triggerScenarios":"Calling build_fcpxml/build_edl with a clip whose 'duration' is 0, missing (defaults to 0.0), negative, or a non-numeric string that float() coerces oddly.","commonSituations":"Duration computed from a failed metadata probe returning 0; placeholder clips with duration 0; unit mistakes (frames given where seconds expected producing 0 after rounding); missing 'duration' key entirely.","solutions":["Provide a positive 'duration' in seconds for each clip.","Probe actual media durations (ffprobe) instead of hardcoding 0.","Guard before building: `assert all(c.get('duration', 0) > 0 for c in clips)`.","Fix unit conversion so durations are seconds, not frames or milliseconds misread."],"exampleFix":"// before\n{\"path\": \"a.mp4\", \"duration\": 0}\n// after\n{\"path\": \"a.mp4\", \"duration\": 5.0}","handlingStrategy":"validation","validationCode":"def validate_clip_durations(clips):\n    for i, c in enumerate(clips):\n        try:\n            dur = float(c.get('duration') or 0.0)\n        except (TypeError, ValueError):\n            raise ValueError(f'clip {i} duration not numeric')\n        if dur <= 0:\n            raise ValueError(f'clip {i} non-positive duration {dur!r}')\n    return True","typeGuard":"def has_positive_duration(c) -> bool:\n    try:\n        return float(c.get('duration') or 0.0) > 0\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    edl = build_edl(clips, fps=30)\nexcept ValueError as e:\n    if 'non-positive duration' in str(e):\n        logger.error('bad duration in clips: %s', e)\n        raise","preventionTips":["Probe real media durations with ffprobe instead of hardcoding","Keep durations in seconds consistently (document units)","Require 'duration' explicitly rather than relying on the 0.0 default","Validate clip lists in a preprocessing pass before timeline building"],"tags":["validation","timeline","duration"],"backgroundTag":"value-out-of-range","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}