affaan-m/ECC · error · ValueError
bad timecode
Error message
bad timecode {tc!r} What it means
_tc_to_frames parses an SMPTE-style timecode string with _TC_RE and raises ValueError when the string does not match the expected HH:MM:SS:FF pattern. It is used by parse_edl to convert EDL timecodes into frame counts.
Solutions
- Open the EDL and fix the malformed timecode to match the expected format (e.g. 'HH:MM:SS:FF')
- Check _TC_RE in export.py to confirm the exact accepted pattern including drop-frame ';' separators
- Pre-clean source EDLs from non-standard exporters or re-export with standard timecode notation
Example fix
// before
edl = parse_edl("clip.tc = '1:23:45:6'") # single-digit fields
// after
edl = parse_edl("clip.tc = '01:23:45:06'") # zero-padded HH:MM:SS:FF Defensive patterns
Strategy: validation
Validate before calling
import re
_TC = re.compile(r"^\d{2}:\d{2}:\d{2}[:;]\d{2}$")
assert _TC.match(tc), f"malformed timecode: {tc!r}" Type guard
def is_valid_timecode(tc) -> bool:
return isinstance(tc, str) and bool(_TC_RE.match(tc)) Try / catch
try:
frames = _tc_to_frames(tc, fps, drop)
except ValueError as e:
print(f"EDL line has bad timecode: {e}; fix the source EDL")
continue Prevention
- Validate every timecode field against the expected regex before parsing the EDL
- Normalize drop-frame ';' notation and zero-pad single-digit fields during pre-processing
- Keep a lint script that scans EDLs for non-conforming timecodes
When it happens
Trigger: parse_edl encounters a timecode field that is not in the expected format, e.g. '1:23:45:06' (single-digit hour if the regex requires two), '00:00:00;05' with a semicolon if the regex only accepts colons, or a truncated/garbage value like 'N/A'.
Common situations: Hand-edited EDL files, exported EDLs from NLEs using drop-frame semicolon notation or different field separators, and blank timecode cells in sparse EDLs.
Related errors
- Cannot read JSON object
- Duplicate memory frontmatter field in
- requires a value
- requires a value
- gh returned invalid JSON
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/72f73b6f49be905f.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/export.py:246
# ---------------------------------------------------------------------------
# EDL parsing (round-trip validation)
# ---------------------------------------------------------------------------
_TC_RE = re.compile(r"^(\d{2}):(\d{2}):(\d{2})[:;](\d{2})$")
_EVENT_RE = re.compile(
r"^(\d+)\s+(\S+)\s+V\s+C\s+"
r"(\d{2}:\d{2}:\d{2}[:;]\d{2})\s+"
r"(\d{2}:\d{2}:\d{2}[:;]\d{2})\s+"
r"(\d{2}:\d{2}:\d{2}[:;]\d{2})\s+"
r"(\d{2}:\d{2}:\d{2}[:;]\d{2})\s*$"
)
def _tc_to_frames(tc: str, fps: float | Fraction, drop: bool) -> int:
m = _TC_RE.match(tc)
if not m:
raise ValueError(f"bad timecode {tc!r}")
hh, mm, ss, ff = (int(g) for g in m.groups())
rate = int(round(float(fps_fraction(fps))))
displayed = (hh * 3600 + mm * 60 + ss) * rate + ff
if drop:
dropped = int(round(float(fps_fraction(fps)) * 0.066666))
total_minutes = hh * 60 + mm
# Skipped frame numbers before this TC: 2/min except every 10th min.
skipped = dropped * (total_minutes - total_minutes // 10)
return displayed - skipped
return displayed
def parse_edl(text: str, fps: float = 24.0) -> list[dict]:
"""Parse a CMX3600 EDL back into structured events (round-trip check)."""
drop = "FCM: DROP FRAME" in text
events: list[dict] = []
for line in text.splitlines():
m = _EVENT_RE.match(line)View on GitHub (pinned to 8321021c54)