nexu-io/open-design · error · SystemExit
{state} row needs {frame_count} frames, found {len(files)} u
Error message
{state} row needs {frame_count} frames, found {len(files)} under {root} What it means
Thrown by compose_from_frames() when the number of frame images found for a given animation state/row is less than that row's required frame_count (defined in ROW_SPECS). Each state has a fixed slot count (e.g. idle=6, running-right=8); too few frames would leave blank cells and break the animation, so the script aborts.
Source
Thrown at skills/hatch-pet/scripts/compose_atlas.py:101
)
source = source.resize((ATLAS_WIDTH, ATLAS_HEIGHT), Image.Resampling.LANCZOS)
atlas = Image.new("RGBA", (ATLAS_WIDTH, ATLAS_HEIGHT), (0, 0, 0, 0))
for _state, row, frame_count in ROW_SPECS:
for column in range(frame_count):
left = column * CELL_WIDTH
top = row * CELL_HEIGHT
cell = source.crop((left, top, left + CELL_WIDTH, top + CELL_HEIGHT))
atlas.alpha_composite(cell, (left, top))
return atlas
def compose_from_frames(root: Path) -> Image.Image:
atlas = Image.new("RGBA", (ATLAS_WIDTH, ATLAS_HEIGHT), (0, 0, 0, 0))
for state, row, frame_count in ROW_SPECS:
files = find_row_frames(root, state, row)
if len(files) < frame_count:
raise SystemExit(
f"{state} row needs {frame_count} frames, found {len(files)} under {root}"
)
for column, frame_path in enumerate(files[:frame_count]):
with Image.open(frame_path) as frame:
paste_centered(atlas, frame, row, column)
return atlas
def save_outputs(atlas: Image.Image, output: Path, webp_output: Path | None) -> None:
output.parent.mkdir(parents=True, exist_ok=True)
atlas.save(output)
if webp_output is not None:
webp_output.parent.mkdir(parents=True, exist_ok=True)
atlas.save(webp_output, format="WEBP", lossless=True, quality=100, method=6)
def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)View on GitHub (pinned to 5be4028344)
Solutions
- Ensure each state has at least its required frame count: idle=6, running-right=8, running-left=8, waving=4, jumping=5, failed=8, waiting=6, running=6, review=6.
- Name frames so find_row_frames matches them: a per-state subfolder named after the state, or filenames like <state>_001.png / <state>-001.png.
- Confirm all frames use a recognized suffix (.png, .webp, .jpg, .jpeg).
Example fix
# before: frames-root/idle/ has only 3 pngs (needs 6) # after: add idle_004.png, idle_005.png, idle_006.png under frames-root/idle/
Defensive patterns
Strategy: validation
Validate before calling
ROW_SPECS = [("idle",0,6),("running-right",1,8),("running-left",2,8),("waving",3,4),("jumping",4,5),("failed",5,8),("waiting",6,6),("running",7,6),("review",8,6)]
SUFFIXES = {".png",".webp",".jpg",".jpeg"}
def assert_frames_complete(root):
from pathlib import Path
root = Path(root)
for state, row, count in ROW_SPECS:
sub = root / state
found = [p for p in sub.iterdir() if p.suffix.lower() in SUFFIXES] if sub.is_dir() else [p for p in root.glob(f"{state}_*") if p.suffix.lower() in SUFFIXES]
if len(found) < count:
raise SystemExit(f"{state} needs {count}, found {len(found)}") Prevention
- Name frames with the state prefix and a zero-padded index (e.g. idle_001.png).
- Keep all frames in recognized formats (.png/.webp/.jpg/.jpeg).
When it happens
Trigger: Run compose_atlas.py --frames-root <dir> where one of the state subdirectories (or matching glob patterns like idle_*, running-right_*, row-N) contains fewer images than ROW_SPECS demands for that state.
Common situations: Incomplete frame export (a render job finished early); files named with a pattern the finder does not recognize (see find_row_frames candidates); mixing extensions not in IMAGE_SUFFIXES (.bmp, .tiff); frames split across multiple folders.
Related errors
- source atlas must be {ATLAS_WIDTH}x{ATLAS_HEIGHT}; got {sour
- refusing to resize source atlas because its aspect ratio doe
- job manifest not found: {path}
- invalid imagegen-jobs.json: jobs must be a list
- unknown job id: {job_id}
AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12).
Data as JSON: /api/errors/ca4f5f0c61e1bcc0.
Report an issue: GitHub.