nexu-io/open-design · error · SystemExit

expected {ATLAS_SIZE[0]}x{ATLAS_SIZE[1]}, got {image.width}x

Error message

expected {ATLAS_SIZE[0]}x{ATLAS_SIZE[1]}, got {image.width}x{image.height}

What it means

Raised by validate_spritesheet in package_custom_pet.py when the opened spritesheet's pixel size does not equal ATLAS_SIZE (1536x1872). The Codex pet atlas contract requires exactly 8 columns x 9 rows of 192x208 cells, so any other dimensions are rejected before packaging.

Source

Thrown at skills/hatch-pet/scripts/package_custom_pet.py:32

ATLAS_SIZE = (1536, 1872)


def default_codex_home() -> Path:
    return Path(os.environ.get("CODEX_HOME") or "~/.codex").expanduser().resolve()


def slugify(value: str) -> str:
    value = value.strip().lower()
    value = re.sub(r"[^a-z0-9]+", "-", value)
    value = re.sub(r"-{2,}", "-", value)
    return value.strip("-")


def validate_spritesheet(path: Path) -> str:
    with Image.open(path) as image:
        if image.size != ATLAS_SIZE:
            raise SystemExit(
                f"expected {ATLAS_SIZE[0]}x{ATLAS_SIZE[1]}, got {image.width}x{image.height}"
            )
        if image.format not in {"PNG", "WEBP"}:
            raise SystemExit(f"expected PNG or WebP, got {image.format}")
        return str(image.format)


def write_webp_spritesheet(source: Path, target: Path, source_format: str) -> None:
    if source_format == "WEBP":
        shutil.copy2(source, target)
        return
    with Image.open(source) as image:
        target.parent.mkdir(parents=True, exist_ok=True)
        image.convert("RGBA").save(
            target,
            format="WEBP",
            lossless=True,
            quality=100,

View on GitHub (pinned to 5be4028344)

Solutions

  1. Re-export the spritesheet at exactly 1536x1872 with 8 columns x 9 rows of 192x208 cells.
  2. If you only have the base sprite, run the full prepare_pet_run.py + generate_pet_images.py pipeline to produce all row strips, then assemble them into the 1536x1872 atlas.
  3. Verify with: python -c "from PIL import Image; print(Image.open('sheet.png').size)".
  4. Re-run package_custom_pet.py with the corrected sheet.

Example fix

// before
python package_custom_pet.py --spritesheet base.png ...
# SystemExit: expected 1536x1872, got 1024x1024

// after - resize is NOT enough; rebuild the full atlas
# produce all states, then assemble into a 1536x1872 (8 cols x 9 rows of 192x208) sheet
python package_custom_pet.py --spritesheet full_atlas.png ...
Defensive patterns

Strategy: validation

Validate before calling

from PIL import Image

ATLAS_SIZE = (1536, 1872)

def check_size(path):
    with Image.open(path) as im:
        if im.size != ATLAS_SIZE:
            raise SystemExit(f"expected {ATLAS_SIZE[0]}x{ATLAS_SIZE[1]}, got {im.size[0]}x{im.size[1]}")

Type guard

def is_atlas_size(path) -> bool:
    with Image.open(path) as im:
        return im.size == (1536, 1872)

Prevention

When it happens

Trigger: Passing a spritesheet that was exported at a different resolution, with the wrong row/column count, with non-uniform cell sizing, or a single-frame image.

Common situations: Hand-authored art exported at 1024x1024 or another common model output size; a spritesheet built against an older atlas layout; an image that was upscaled/downscaled; using a generated base sprite (1024x1024 default) directly instead of assembling the full atlas.

Related errors


AI-assisted analysis of nexu-io/open-design@5be4028344 (2026-08-12). Data as JSON: /api/errors/db575a172ba41b81. Report an issue: GitHub.