affaan-m/ECC · error · ValueError
placement id must be unique and nonempty
Error message
placement id must be unique and nonempty
What it means
allocate_placements validates each placement event dict; it raises ValueError('placement id must be unique and nonempty') when an event's 'id' is missing, not a string, an empty string, or duplicates an id already seen in the same call. Ids identify composited placements downstream, so they must be present and unique.
Solutions
- Ensure every placement dict has a nonempty string 'id' and that ids are unique within the list
- When generating placements in a loop, append an index or uuid: f'overlay-{i}' or str(uuid4())
- Check for casing/key typos: the key must be exactly 'id'
- Deduplicate or rename conflicting ids before calling, e.g. by prefixing duplicates
Example fix
# before
placements = [{'id': 'logo', 'asset': 'a.webm'}, {'id': 'logo', 'asset': 'b.webm'}]
# after
placements = [{'id': 'logo-a', 'asset': 'a.webm'}, {'id': 'logo-b', 'asset': 'b.webm'}] Defensive patterns
Strategy: validation
Validate before calling
def validate_placement_ids(placements):
seen = set()
for ev in placements:
pid = ev.get('id')
if not isinstance(pid, str) or not pid or pid in seen:
raise ValueError(f'bad placement id: {pid!r}')
seen.add(pid)
validate_placement_ids(placements) Type guard
def has_valid_id(ev) -> bool:
pid = ev.get('id')
return isinstance(pid, str) and bool(pid) Try / catch
try:
resolve.allocate_placements(fps, base_track_count, placements)
except ValueError as e:
if 'placement id' in str(e):
raise PlacementError('duplicate or missing placement id') from e
raise Prevention
- Generate ids programmatically with an index/uuid in loops
- Lint placement JSON for duplicate ids before rendering
- Keep the key spelled exactly 'id' in configs
When it happens
Trigger: Calling allocate_placements/apply_placements/plan with placements where one event lacks 'id', has id='' or id=None, has a numeric id, or two events share the same id (e.g. a template loop forgot to uniquify ids).
Common situations: Programmatically generated placement lists reusing the default id across iterations; JSON configs with a typo'd key ('Id' vs 'id'); copying a placement entry without renaming its id.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- asset must be a local regular file
- invalid canonical health evidence snapshot
- Invalid : expected globally unique id
- Invalid managed hook entry at
- invalid plan-canvas session key
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/09d3d28bb59bbe36.
Report an issue: GitHub.
Appendix: source
Thrown at skills/taste-application/scripts/tasteforge/resolve.py:100
"fps": str(average),
"frames": int(stream["nb_read_frames"]),
"has_alpha": alpha,
}
def allocate_placements(placements, *, fps, base_track_count, probe=probe_asset):
"""Validate assets and interval-color overlays above preserved video tracks.
record_frame is an absolute timeline frame; intervals are [start, end).
Optional requires_alpha=True enforces a decoded alpha-capable pixel format.
"""
rate = _fps(fps)
_integer(base_track_count, "base_track_count")
checked, seen, metadata = [], set(), {}
for event in placements:
identifier = event.get("id")
if not isinstance(identifier, str) or not identifier or identifier in seen:
raise ValueError("placement id must be unique and nonempty")
seen.add(identifier)
start = _integer(event.get("record_frame"), "record_frame")
frames = _integer(event.get("frames"), "frames", 1)
opacity = event.get("opacity")
if (
isinstance(opacity, bool)
or not isinstance(opacity, (int, float))
or not math.isfinite(opacity)
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}")View on GitHub (pinned to 8321021c54)