{"record":{"id":"c94bb312c6392272","repo":"stablyai/orca","slug":"could-not-save-the-pet","errorCode":null,"errorMessage":"Could not save the pet.","messagePattern":"Could not save the pet\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/pet.ts","lineNumber":220,"sourceCode":"      throw new Error('Selected path is not a file')\n    }\n    if (srcStat.size > MAX_BYTES) {\n      throw new Error(\n        `File is too large (${(srcStat.size / (1024 * 1024)).toFixed(1)} MB). Max is ${MAX_BYTES / (1024 * 1024)} MB.`\n      )\n    }\n\n    const dir = getPetsDir()\n    await mkdir(dir, { recursive: true })\n    const id = randomUUID()\n    // Why: keep the original extension in the on-disk name so pet:read can rebuild the Blob MIME without a separate lookup.\n    const fileName = `${id}${classified.ext}`\n    const dest = join(dir, fileName)\n    try {\n      await copyFile(src, dest)\n    } catch {\n      await rm(dest, { force: true }).catch(() => {})\n      throw new Error('Could not save the pet.')\n    }\n\n    const rawLabel = basename(src, extname(src)).trim()\n    const label = rawLabel.length > 0 ? rawLabel.slice(0, 40) : 'Custom pet'\n    return {\n      id,\n      label,\n      fileName,\n      mimeType: classified.mimeType,\n      kind: 'image'\n    }\n  })\n\n  ipcMain.handle('pet:importPetBundle', async (event): Promise<CustomPet | null> => {\n    const senderWindow =\n      BrowserWindow.fromWebContents(event.sender) ?? BrowserWindow.getFocusedWindow()\n    // Why: the bundle is a folder, but Finder may let users pick `pet.json` inside it — post-pick logic walks up to the parent.\n    const options: Electron.OpenDialogOptions = {","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/pet.ts#L202-L238","documentation":"Thrown by the pet:import handler when copyFile(src, dest) rejects while copying the validated image into the userData/sidekicks/custom directory. The handler cleans up the partial destination (rm with force, errors swallowed) before rethrowing, so no half-written file is left behind. The collapsed message hides the underlying errno (ENOSPC, EACCES, EROFS, EIO).","triggerScenarios":"The disk holding userData is full (ENOSPC), the pets directory is read-only or permission-restricted (EACCES/EROFS), the destination volume was disconnected, or an AV/backup process locked the file on Windows.","commonSituations":"Low disk space on the system drive, userData redirected to a network share that dropped, sandbox/permissions tightening on macOS userData, or Windows antivirus locking the new file mid-copy.","solutions":["Free disk space on the volume holding the app's userData directory and retry the import.","Confirm the userData/sidekicks/custom directory is writable by the Electron process.","If userData is on a network/removable volume, move it to local storage or remount and retry.","On Windows, temporarily disable aggressive AV scanning of the userData folder and retry."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import { statfs } from 'node:fs/promises'\n\nasync function hasDiskSpace(dir: string, need = 64 * 1024 * 1024): Promise<boolean> {\n  try {\n    const { bavail, bsize } = await statfs(dir)\n    return bavail * bsize > need\n  } catch {\n    return true\n  }\n}\n\nif (!(await hasDiskSpace(userDataDir))) {\n  notify('Not enough disk space to save the pet.')\n  return\n}","typeGuard":null,"tryCatchPattern":"try {\n  await ipcRenderer.invoke('pet:import')\n} catch (e) {\n  if (e instanceof Error && e.message === 'Could not save the pet.') {\n    notify('Saving failed. Check disk space and permissions, then try again.')\n  } else throw e\n}","preventionTips":["Keep free space on the userData volume.","Confirm userData/sidekicks/custom is writable.","On Windows, exclude the userData folder from aggressive AV scanning."],"tags":["ipc","pet-overlay","filesystem","disk-space","asset-import"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}