stablyai/orca · error · Error
Could not decode the spritesheet image.
Error message
Could not decode the spritesheet image.
What it means
Thrown at pet.ts:335-337 when readSheetDimensions(sheetBuf) returns null/undefined, meaning the image bytes could not be decoded or the decoder could not extract width/height. This runs only when manifest.frame is set (the decode path at line 328). The comment at line 329 notes nativeImage can fail on some WebP variants in headless contexts.
Solutions
- Re-export the image with a real encoder so the bytes match the extension.
- Open the file in an image viewer to confirm it is valid and not truncated.
- If using WebP, try converting to PNG/APNG which have broader decoder support.
- Verify the file is not zero-length.
Example fix
# before — cat.jpg renamed to cat.png mv cat.jpg cat.png # after — re-encode properly convert cat.jpg cat.png
Defensive patterns
Strategy: validation
Validate before calling
import { readFile } from 'node:fs/promises'
import { nativeImage } from 'electron'
async function assertSheetDecodes(p: string) {
const buf = await readFile(p)
const img = nativeImage.createFromBuffer(buf)
const size = img.getSize()
if (!size.width || !size.height) throw new Error('spritesheet does not decode')
} Try / catch
try { await importPetBundle(p) }
catch (e) { if (e instanceof Error && e.message === 'Could not decode the spritesheet image.') { /* re-encode the image */ } else throw e } Prevention
- Re-export images through a real encoder rather than renaming extensions.
- Open the file in an image viewer before bundling to confirm it is valid.
- Prefer PNG/APNG over exotic WebP variants for maximum decoder compatibility.
When it happens
Trigger: The file passed the extension check (1245) but its bytes are not a valid decodable image of that format: a .png that is actually a JPEG, a truncated/corrupt image, an unsupported WebP variant, or a zero-byte file that somehow passed stat.
Common situations: File renamed without re-encoding (e.g. cat.jpg renamed to cat.png); a partially downloaded/truncated image; an exotic WebP/animated format the decoder rejects; a file with correct extension but wrong magic bytes.
Related errors
- Animation " " declares frame durations but frames.
- Animation " " has frames but sheet only has columns.
- Animation " " references row but sheet has .
- Artifact create recovery record exceeds the supported size.
- Artifact share records could not be read safely.
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/a96abc97190c44c3.
Report an issue: GitHub.
Appendix: source
Thrown at src/main/ipc/pet.ts:337
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) {
// Why: only decode when a frame layout needs validating — nativeImage can fail on some WebP variants in headless contexts.
const sheetBuf = await readFile(sheetSrc)
// Why: defend against TOCTOU — file may have grown between stat and read.
if (sheetBuf.byteLength > MAX_BYTES) {
throw new Error('Spritesheet exceeded the size limit.')
}
const dims = await readSheetDimensions(sheetBuf)
if (!dims) {
throw new Error('Could not decode the spritesheet image.')
}
const { width: fw, height: fh } = manifest.frame
if (dims.width % fw !== 0 || dims.height % fh !== 0) {
throw new Error(
`Spritesheet ${dims.width}×${dims.height} is not a clean multiple of frame ${fw}×${fh}.`
)
}
const columns = dims.width / fw
const rows = dims.height / fh
if (manifest.animations) {
for (const [name, anim] of Object.entries(manifest.animations)) {
if (anim.row >= rows) {
throw new Error(`Animation "${name}" references row ${anim.row} but sheet has ${rows}.`)
}
if (anim.frames > columns) {
throw new Error(
`Animation "${name}" has ${anim.frames} frames but sheet only has ${columns} columns.`
)View on GitHub (pinned to 1136503c6a)