{"record":{"id":"9afae6993608fd49","repo":"stablyai/orca","slug":"spritesheet-file-not-found","errorCode":null,"errorMessage":"Spritesheet file not found.","messagePattern":"Spritesheet file not found\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/pet.ts","lineNumber":316,"sourceCode":"    const bundleRoot = bundleResolved + sep\n    // Why: Windows volumes are case-insensitive; lowercase the prefix compare so case differences can't bypass the escape check.\n    const cmp = process.platform === 'win32' ? (s: string) => s.toLowerCase() : (s: string) => s\n    if (!cmp(sheetSrc + sep).startsWith(cmp(bundleRoot))) {\n      throw new Error('spritesheetPath escapes the bundle.')\n    }\n    if (await isSymlink(sheetSrc)) {\n      throw new Error('spritesheet must not be a symlink.')\n    }\n    const sheetClass = classifyFile(sheetSrc)\n    if (!sheetClass || sheetClass.ext === '.svg') {\n      // SVG can't be used as a sprite sheet (no pixel grid).\n      throw new Error('Spritesheet must be a PNG, APNG, JPG, GIF, or WebP.')\n    }\n    let sheetStat: Awaited<ReturnType<typeof stat>>\n    try {\n      sheetStat = await stat(sheetSrc)\n    } catch {\n      throw new Error('Spritesheet file not found.')\n    }\n    if (!sheetStat.isFile()) {\n      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      }","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/pet.ts#L298-L334","documentation":"Thrown at pet.ts:313-316 when stat(sheetSrc) rejects (any fs error, most commonly ENOENT). This fires after the path is confirmed in-bundle and not a symlink but before checking whether it is a file — so the file simply does not exist at the resolved location.","triggerScenarios":"pet.json's spritesheetPath names a file that is not present in the bundle directory; the file was deleted between the bundle listing and the import; the path has a typo (e.g. 'spritestheepng').","commonSituations":"Typo in spritesheetPath; bundle was partially copied (missing the image); case-sensitivity mismatch on a case-sensitive filesystem (bundle was 'Cat.png' but manifest says 'cat.png') after copying from macOS/Windows.","solutions":["Verify the exact filename (including case) exists in the bundle directory and matches spritesheetPath.","On case-sensitive filesystems (Linux), align the case in spritesheetPath with the actual file.","Re-copy the full bundle directory so the image is present."],"exampleFix":"# before — manifest says cat.png but file is Cat.png on Linux\n{ \"spritesheetPath\": \"cat.png\" }\n# after\nmv bundle/Cat.png bundle/cat.png  # or update manifest to \"Cat.png\"","handlingStrategy":"validation","validationCode":"import { stat } from 'node:fs/promises'\nasync function assertSheetExists(p: string) {\n  try { await stat(p) } catch { throw new Error(`spritesheet not found: ${p}`) }\n}","typeGuard":null,"tryCatchPattern":"try { await importPetBundle(p) }\ncatch (e) { if (e instanceof Error && e.message === 'Spritesheet file not found.') { /* check filename/case */ } else throw e }","preventionTips":["On case-sensitive filesystems, keep filenames lowercase and match the manifest exactly.","Verify the bundle directory contains the spritesheet file before importing."],"tags":["pet-bundle","filesystem","enoent","case-sensitivity"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}