{"record":{"id":"a96abc97190c44c3","repo":"stablyai/orca","slug":"could-not-decode-the-spritesheet-image","errorCode":null,"errorMessage":"Could not decode the spritesheet image.","messagePattern":"Could not decode the spritesheet image\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/pet.ts","lineNumber":337,"sourceCode":"      throw new Error('Spritesheet path is not a file.')\n    }\n    if (sheetStat.size > MAX_BYTES) {\n      throw new Error(\n        `Spritesheet is too large (${(sheetStat.size / (1024 * 1024)).toFixed(1)} MB).`\n      )\n    }\n\n    let sprite: NonNullable<CustomPet['sprite']> | undefined\n    if (manifest.frame) {\n      // Why: only decode when a frame layout needs validating — nativeImage can fail on some WebP variants in headless contexts.\n      const sheetBuf = await readFile(sheetSrc)\n      // Why: defend against TOCTOU — file may have grown between stat and read.\n      if (sheetBuf.byteLength > MAX_BYTES) {\n        throw new Error('Spritesheet exceeded the size limit.')\n      }\n      const dims = await readSheetDimensions(sheetBuf)\n      if (!dims) {\n        throw new Error('Could not decode the spritesheet image.')\n      }\n      const { width: fw, height: fh } = manifest.frame\n      if (dims.width % fw !== 0 || dims.height % fh !== 0) {\n        throw new Error(\n          `Spritesheet ${dims.width}×${dims.height} is not a clean multiple of frame ${fw}×${fh}.`\n        )\n      }\n      const columns = dims.width / fw\n      const rows = dims.height / fh\n      if (manifest.animations) {\n        for (const [name, anim] of Object.entries(manifest.animations)) {\n          if (anim.row >= rows) {\n            throw new Error(`Animation \"${name}\" references row ${anim.row} but sheet has ${rows}.`)\n          }\n          if (anim.frames > columns) {\n            throw new Error(\n              `Animation \"${name}\" has ${anim.frames} frames but sheet only has ${columns} columns.`\n            )","sourceCodeStart":319,"sourceCodeEnd":355,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/pet.ts#L319-L355","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before — cat.jpg renamed to cat.png\nmv cat.jpg cat.png\n# after — re-encode properly\nconvert cat.jpg cat.png","handlingStrategy":"validation","validationCode":"import { readFile } from 'node:fs/promises'\nimport { nativeImage } from 'electron'\nasync function assertSheetDecodes(p: string) {\n  const buf = await readFile(p)\n  const img = nativeImage.createFromBuffer(buf)\n  const size = img.getSize()\n  if (!size.width || !size.height) throw new Error('spritesheet does not decode')\n}","typeGuard":null,"tryCatchPattern":"try { await importPetBundle(p) }\ncatch (e) { if (e instanceof Error && e.message === 'Could not decode the spritesheet image.') { /* re-encode the image */ } else throw e }","preventionTips":["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."],"tags":["pet-bundle","image-decode","corruption"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}