stablyai/orca · error · Error

Could not save the pet.

Error message

Could not save the pet.

What it means

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).

Solutions

  1. Free disk space on the volume holding the app's userData directory and retry the import.
  2. Confirm the userData/sidekicks/custom directory is writable by the Electron process.
  3. If userData is on a network/removable volume, move it to local storage or remount and retry.
  4. On Windows, temporarily disable aggressive AV scanning of the userData folder and retry.
Defensive patterns

Strategy: try-catch

Validate before calling

import { statfs } from 'node:fs/promises'

async function hasDiskSpace(dir: string, need = 64 * 1024 * 1024): Promise<boolean> {
  try {
    const { bavail, bsize } = await statfs(dir)
    return bavail * bsize > need
  } catch {
    return true
  }
}

if (!(await hasDiskSpace(userDataDir))) {
  notify('Not enough disk space to save the pet.')
  return
}

Try / catch

try {
  await ipcRenderer.invoke('pet:import')
} catch (e) {
  if (e instanceof Error && e.message === 'Could not save the pet.') {
    notify('Saving failed. Check disk space and permissions, then try again.')
  } else throw e
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12). Data as JSON: /api/errors/c94bb312c6392272. Report an issue: GitHub.

Appendix: source

Thrown at src/main/ipc/pet.ts:220

      throw new Error('Selected path is not a file')
    }
    if (srcStat.size > MAX_BYTES) {
      throw new Error(
        `File is too large (${(srcStat.size / (1024 * 1024)).toFixed(1)} MB). Max is ${MAX_BYTES / (1024 * 1024)} MB.`
      )
    }

    const dir = getPetsDir()
    await mkdir(dir, { recursive: true })
    const id = randomUUID()
    // Why: keep the original extension in the on-disk name so pet:read can rebuild the Blob MIME without a separate lookup.
    const fileName = `${id}${classified.ext}`
    const dest = join(dir, fileName)
    try {
      await copyFile(src, dest)
    } catch {
      await rm(dest, { force: true }).catch(() => {})
      throw new Error('Could not save the pet.')
    }

    const rawLabel = basename(src, extname(src)).trim()
    const label = rawLabel.length > 0 ? rawLabel.slice(0, 40) : 'Custom pet'
    return {
      id,
      label,
      fileName,
      mimeType: classified.mimeType,
      kind: 'image'
    }
  })

  ipcMain.handle('pet:importPetBundle', async (event): Promise<CustomPet | null> => {
    const senderWindow =
      BrowserWindow.fromWebContents(event.sender) ?? BrowserWindow.getFocusedWindow()
    // Why: the bundle is a folder, but Finder may let users pick `pet.json` inside it — post-pick logic walks up to the parent.
    const options: Electron.OpenDialogOptions = {

View on GitHub (pinned to 1136503c6a)