affaan-m/ECC · error · ValueError
asset fps differs from timeline
Error message
asset fps differs from timeline: {asset} What it means
For each placement, allocate_placements compares the probed asset fps (via metadata[asset]) with the timeline fps and raises ValueError('asset fps differs from timeline: {asset}') when they are not exactly equal after normalization. Compositing assumes every overlay shares the timeline's frame rate, or frame indexing would drift.
Solutions
- Re-encode the asset to the timeline fps: `ffmpeg -i asset.mp4 -vf fps=30 -c:v libx264 -crf 18 asset_30fps.mp4`
- Or set the timeline fps to match the asset's probed fps when all assets share one rate
- Check the probed fps (ffprobe avg_frame_rate) for each asset and pick a timeline fps all assets can be conformed to
- If using a metadata cache, invalidate and re-probe assets that were re-encoded
Example fix
# before plan = resolve.plan(placements, fps=30) # asset is 25 fps # after subprocess.run(['ffmpeg', '-i', 'overlay.mp4', '-vf', 'fps=30', 'overlay_30.mp4']) plan = resolve.plan(placements, fps=30) # asset now 30 fps
Defensive patterns
Strategy: validation
Validate before calling
import json, subprocess
from fractions import Fraction
def asset_fps(path):
r = subprocess.run(['ffprobe', '-v', 'error', '-select_streams', 'v:0',
'-show_entries', 'stream=avg_frame_rate', '-of', 'json',
str(path)], capture_output=True, text=True)
s = json.loads(r.stdout)['streams'][0]['avg_frame_rate']
return float(Fraction(s))
timeline = 30.0
for ev in placements:
assert asset_fps(ev['asset']) == timeline, f"{ev['asset']} not {timeline} fps" Try / catch
try:
resolve.plan(placements, fps=fps)
except ValueError as e:
if 'differs from timeline' in str(e):
asset = e.args[0].rsplit(': ', 1)[1]
raise AssetError(f'{asset} must be re-encoded to {fps} fps') from e
raise Prevention
- Standardize a project fps and re-encode all overlays to it at ingest
- Probe fps for every asset during ingest, not at render time
- Invalidate metadata caches after re-encoding assets
When it happens
Trigger: Timeline at 30 fps but overlay asset encoded at 24/25/29.97/60 fps; rational representations that normalize differently (e.g. '30000/1001' vs '30'); metadata supplied via probe() containing a stale or wrong fps field.
Common situations: Mixing stock footage recorded at 25 fps (PAL) into a 30 fps project; phone footage at 29.97; templates assuming a single fps while assets were exported at another; reused metadata cache from an older version of the asset.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- fps must be positive, got
- source fps must be finite and positive; provide --fps
- analyze() needs at least one frame
- analyze() needs at least one frame
- asset must contain a video stream
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/0e5e3ce9eaddc8e1.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/resolve.py:127
or not 0 <= opacity <= 100
):
raise ValueError("explicit opacity must be finite within 0..100")
composite = _integer(event.get("composite"), "composite")
raw_asset = event.get("asset")
if not isinstance(raw_asset, (str, Path)) or not str(raw_asset):
raise ValueError("asset must be a local regular file")
asset = Path(raw_asset).expanduser().resolve()
if not asset.is_file():
raise ValueError(f"asset must be a local regular file: {asset}")
alpha = event.get("requires_alpha", False)
if not isinstance(alpha, bool):
raise ValueError("requires_alpha must be boolean")
key = str(asset)
if key not in metadata:
metadata[key] = probe(asset)
info = metadata[key]
if _fps(info.get("fps")) != rate:
raise ValueError(f"asset fps differs from timeline: {asset}")
if _integer(info.get("frames"), "asset frames", 1) < frames:
raise ValueError(f"asset contains too few frames: {asset}")
if alpha and info.get("has_alpha") is not True:
raise ValueError(f"asset requires verified alpha: {asset}")
checked.append(
{
"id": identifier,
"asset": key,
"record_frame": start,
"frames": frames,
"opacity": opacity,
"composite": composite,
}
)
if not checked:
raise ValueError("at least one placement is required")
ends, allocated = [], {}
for event in sorted(checked, key=lambda entry: entry["record_frame"]):View on GitHub (pinned to 8321021c54)