affaan-m/ECC · error · ValueError
at least one placement is required
Error message
at least one placement is required
What it means
allocate_placements refuses to produce a plan with zero placements. After validating each entry, if none survived (or the input list was empty), it throws because downstream allocation, track assignment, and readback all assume at least one event.
Solutions
- Pass at least one placement dict with asset, start, end
- Guard the call site: skip apply entirely when the computed list is empty
- Fix the upstream filtering logic that removed all placements
- Validate placements input before calling
Example fix
// before
apply_placements(timeline, pool, placements=filtered) # filtered == []
// after
if not filtered:
return # nothing to apply
apply_placements(timeline, pool, placements=filtered) Defensive patterns
Strategy: validation
Validate before calling
if not placements:
raise ValueError("no placements to apply; check upstream filtering") Type guard
def is_nonempty_list(v) -> bool:
return isinstance(v, list) and len(v) > 0 Prevention
- Skip apply_placements entirely when the computed list is empty
- Log placement-list size at build time
- Add an assertion after any filtering step that builds placements
- Fail fast in config loading if the placements array is empty
When it happens
Trigger: Calling allocate_placements/apply_placements/plan with an empty placements list, or with a list where all entries were filtered out before this check.
Common situations: Programmatically built placement lists that ended up empty due to an upstream filter; config file with a placeholder empty array; a caller passing None coerced to [].
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- at least one explicit seed is required
- At least one guided harness must be selected
- at least one non-empty
- At least one video is required
- commit message cannot be empty
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/68a876864545b273.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/resolve.py:143
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"]):
index = next(
(i for i, end in enumerate(ends) if end <= event["record_frame"]), len(ends)
)
if index == len(ends):
ends.append(0)
ends[index] = event["record_frame"] + event["frames"]
allocated[event["id"]] = {**event, "track": base_track_count + index + 1}
return [allocated[event["id"]] for event in checked]
def _path(item):
media = item.GetMediaPoolItem()
raw = media.GetClipProperty("File Path") if media else None
return str(Path(raw).expanduser().resolve()) if raw else None
View on GitHub (pinned to 8321021c54)