{"record":{"id":"47b466ea771b33f1","repo":"stablyai/orca","slug":"could-not-save-the-pet-bundle","errorCode":null,"errorMessage":"Could not save the pet bundle.","messagePattern":"Could not save the pet bundle\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/pet.ts","lineNumber":397,"sourceCode":"\n    // Why: always a fresh UUID (not the manifest's display-hint id) to avoid collisions, unsafe ids, and re-import clobbering.\n    const id = randomUUID()\n    const root = getPetsDir()\n    await mkdir(root, { recursive: true })\n    const destDir = join(root, id)\n    const sheetExt = sheetClass.ext\n    const sheetFileName = `spritesheet${sheetExt}`\n    // Why: stage into a sibling .tmp then atomically rename, so a mid-copy failure can't leave a half-imported bundle.\n    const tmpDir = `${destDir}.tmp`\n    try {\n      await rm(tmpDir, { recursive: true, force: true }).catch(() => {})\n      await mkdir(tmpDir, { recursive: true })\n      await copyFileNoFollow(sheetSrc, join(tmpDir, sheetFileName))\n      await copyFileNoFollow(manifestPath, join(tmpDir, 'pet.json'))\n      await rename(tmpDir, destDir)\n    } catch {\n      await rm(tmpDir, { recursive: true, force: true }).catch(() => {})\n      throw new Error('Could not save the pet bundle.')\n    }\n\n    const rawLabel = (manifest.displayName ?? manifest.id ?? basename(bundleDir)).trim()\n    const label = rawLabel.length > 0 ? rawLabel.slice(0, 40) : 'Pet bundle'\n    return {\n      id,\n      label,\n      fileName: sheetFileName,\n      mimeType: sheetClass.mimeType,\n      kind: 'bundle',\n      sprite,\n      // Why: renderer falls back to spriteFps when sprite is undefined (detected-frame bundles).\n      ...(manifest.fps !== undefined ? { spriteFps: manifest.fps } : {})\n    }\n  })\n\n  ipcMain.handle(\n    'pet:read',","sourceCodeStart":379,"sourceCodeEnd":415,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/pet.ts#L379-L415","documentation":"Thrown at pet.ts:389-398 when the atomic bundle save fails: the import stages files into a sibling .tmp directory (lines 388-394) then renames it into place. If rm, mkdir, copyFileNoFollow, or rename throws, the catch cleans up the .tmp directory and re-throws this generic message. The original error is swallowed (no logging), so the exact cause (disk full, EPERM, antivirus lock, cross-device rename) is not surfaced.","triggerScenarios":"Disk full during copyFileNoFollow; EPERM/permission denied writing to getPetsDir() (userData/sidekicks/custom); antivirus or file lock blocking the rename on Windows; cross-device rename where the .tmp dir and destDir are on different filesystems; the userData path is read-only.","commonSituations":"Windows antivirus locking the renamed directory; a managed/locked userData directory (corporate machine); low disk space; a stale .tmp from a previous failed import that rm cannot remove (permissions).","solutions":["Check free disk space on the volume holding userData (app.getPath('userData')).","On Windows, temporarily disable/reconfigure antivirus that may lock the rename, or add an exclusion for the userData directory.","Verify write permissions on the userData/sidekicks/custom directory.","Manually remove any leftover <destDir>.tmp directory and retry.","If on a multi-volume setup, ensure the userData directory is not on a read-only mount."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import { access, constants } from 'node:fs/promises'\nimport { app } from 'electron'\nasync function assertPetsDirWritable() {\n  const dir = app.getPath('userData')\n  await access(dir, constants.W_OK)\n}","typeGuard":null,"tryCatchPattern":"try { await importPetBundle(p) }\ncatch (e) {\n  if (e instanceof Error && e.message === 'Could not save the pet bundle.') {\n    // surface a user-facing message: check disk space / permissions / antivirus\n    showError('Could not save the pet bundle. Check disk space and permissions, then retry.')\n  } else throw e\n}","preventionTips":["Free disk space before importing large bundles.","On Windows, add an antivirus exclusion for the app's userData directory.","Ensure the userData directory is writable and not on a read-only mount.","Note the original fs error is swallowed in the catch (pet.ts:395) — if you control the code, log the inner error for diagnostics."],"tags":["pet-bundle","filesystem","atomic-write","permissions","disk-space"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}