stablyai/orca · error · Error

Spritesheet must be a PNG, APNG, JPG, GIF, or WebP.

Error message

Spritesheet must be a PNG, APNG, JPG, GIF, or WebP.

What it means

Thrown at pet.ts:307-310 when classifyFile(sheetSrc) returns null (the extension is not in IMAGE_FORMATS) or the extension is '.svg'. classifyFile lowercases the extension and looks it up in the PNG/APNG/JPG/JPEG/GIF/WebP/SVG map (pet.ts:18-26), but SVG is explicitly rejected here because a sprite sheet needs a pixel grid that SVG lacks.

Source

Thrown at src/main/ipc/pet.ts:310

    // Why: bundles exported on Windows may be imported on macOS/Linux; normalize separators before resolving.
    const sheetSrc = resolve(bundleDir, normalizedSpritePath)
    const bundleResolved = resolve(bundleDir)
    if (sheetSrc === bundleResolved) {
      throw new Error('spritesheetPath must point to a file, not the bundle root.')
    }
    const bundleRoot = bundleResolved + sep
    // Why: Windows volumes are case-insensitive; lowercase the prefix compare so case differences can't bypass the escape check.
    const cmp = process.platform === 'win32' ? (s: string) => s.toLowerCase() : (s: string) => s
    if (!cmp(sheetSrc + sep).startsWith(cmp(bundleRoot))) {
      throw new Error('spritesheetPath escapes the bundle.')
    }
    if (await isSymlink(sheetSrc)) {
      throw new Error('spritesheet must not be a symlink.')
    }
    const sheetClass = classifyFile(sheetSrc)
    if (!sheetClass || sheetClass.ext === '.svg') {
      // SVG can't be used as a sprite sheet (no pixel grid).
      throw new Error('Spritesheet must be a PNG, APNG, JPG, GIF, or WebP.')
    }
    let sheetStat: Awaited<ReturnType<typeof stat>>
    try {
      sheetStat = await stat(sheetSrc)
    } catch {
      throw new Error('Spritesheet file not found.')
    }
    if (!sheetStat.isFile()) {
      throw new Error('Spritesheet path is not a file.')
    }
    if (sheetStat.size > MAX_BYTES) {
      throw new Error(
        `Spritesheet is too large (${(sheetStat.size / (1024 * 1024)).toFixed(1)} MB).`
      )
    }

    let sprite: NonNullable<CustomPet['sprite']> | undefined
    if (manifest.frame) {

View on GitHub (pinned to 1136503c6a)

Solutions

  1. Convert the spritesheet to PNG, APNG, JPG, GIF, or WebP and update spritesheetPath in pet.json.
  2. If you have an SVG, rasterize it to PNG at the target frame size before bundling.
  3. Confirm the extension matches the actual file format — a .png-named BMP will decode-fail later even if it passes this gate.

Example fix

# before
{ "spritesheetPath": "cat.svg" }
# after — rasterize and rename
convert cat.svg -resize 32x32 cat.png
{ "spritesheetPath": "cat.png" }
Defensive patterns

Strategy: validation

Validate before calling

import { extname } from 'node:path'
const SHEET_EXTS = new Set(['.png', '.apng', '.jpg', '.jpeg', '.gif', '.webp'])
function assertSheetFormat(p: string) {
  const ext = extname(p).toLowerCase()
  if (!SHEET_EXTS.has(ext)) throw new Error(`unsupported spritesheet extension: ${ext}`)
}

Type guard

function isSupportedSheetExt(p: string): boolean {
  return SHEET_EXTS.has(extname(p).toLowerCase())
}

Try / catch

try { await importPetBundle(p) }
catch (e) { if (e instanceof Error && e.message === 'Spritesheet must be a PNG, APNG, JPG, GIF, or WebP.') { /* convert the image */ } else throw e }

Prevention

When it happens

Trigger: spritesheetPath points to a file whose extension is not .png/.apng/.jpg/.jpeg/.gif/.webp (e.g. .bmp, .tiff, .avif, no extension), or to an .svg file.

Common situations: Bundle author used an unsupported format; file saved as .svg because the design tool defaulted to vector export; uppercase extension is fine (classifyFile lowercases) but a .bmp slipped in.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/52161f7d0229b4e9. Report an issue: GitHub.