stablyai/orca · warning · Error
Bundle is missing pet.json.
Error message
Bundle is missing pet.json.
What it means
Thrown by the pet:importPetBundle handler when stat(join(bundleDir, 'pet.json')) rejects — the bundle directory exists and was stat'd, but it contains no pet.json at its root. pet.json is the manifest that PetManifestSchema parses (id, displayName, spritesheetPath, frame, animations); without it the bundle cannot be imported.
Source
Thrown at src/main/ipc/pet.ts:262
: await dialog.showOpenDialog(options)
if (result.canceled || result.filePaths.length === 0) {
return null
}
const picked = result.filePaths[0]
let bundleDir: string
try {
const pickedStat = await stat(picked)
bundleDir = pickedStat.isDirectory() ? picked : dirname(picked)
} catch {
throw new Error('Could not read the selected path.')
}
const manifestPath = join(bundleDir, 'pet.json')
let manifestStat: Awaited<ReturnType<typeof stat>>
try {
manifestStat = await stat(manifestPath)
} catch {
throw new Error('Bundle is missing pet.json.')
}
if (!manifestStat.isFile() || manifestStat.size > MAX_MANIFEST_BYTES) {
throw new Error('pet.json is invalid.')
}
if (await isSymlink(manifestPath)) {
throw new Error('pet.json must not be a symlink.')
}
let manifest: ResolvedPetManifest<PetManifest>
try {
const raw = await readFile(manifestPath, 'utf8')
// Why: defend against TOCTOU — the file may have grown between the stat check and this read.
if (Buffer.byteLength(raw, 'utf8') > MAX_MANIFEST_BYTES) {
throw new Error('pet.json exceeded the manifest size limit.')
}
manifest = applyCodexPetDefaults(PetManifestSchema.parse(JSON.parse(raw)))
} catch (error) {
throw new Error(`Invalid pet.json: ${error instanceof Error ? error.message : 'parse error'}`)View on GitHub (pinned to 1136503c6a)
Solutions
- Ensure the picked folder contains a pet.json at its top level (not in a subfolder).
- If the bundle was extracted from an archive, re-extract preserving the top-level pet.json.
- Author a pet.json per the PetManifestSchema (id, displayName, spritesheetPath, optional frame/animations) before importing.
- Pick the bundle folder itself, not a nested subfolder.
Defensive patterns
Strategy: validation
Validate before calling
import { stat } from 'node:fs/promises'
import { join } from 'node:path'
async function bundleHasManifest(bundleDir: string): Promise<boolean> {
try {
return (await stat(join(bundleDir, 'pet.json'))).isFile()
} catch {
return false
}
}
if (!(await bundleHasManifest(bundleDir))) {
notify('This folder is missing pet.json — not a valid pet bundle.')
return
} Try / catch
try {
await ipcRenderer.invoke('pet:importPetBundle')
} catch (e) {
if (e instanceof Error && e.message === 'Bundle is missing pet.json.') {
notify('That folder has no pet.json. Pick a complete .codex-pet bundle.')
} else throw e
} Prevention
- Ensure pet.json sits at the bundle folder's top level.
- Re-extract bundles from archives preserving the top-level manifest.
- Pick the bundle folder itself, not a nested subfolder.
When it happens
Trigger: The picked folder is not a valid .codex-pet bundle (missing manifest), pet.json was renamed/moved/deleted inside the folder, or the user picked a generic folder that merely contains image files.
Common situations: User zipped/unzipped a bundle and the manifest was dropped or nested one level deep, the bundle was hand-authored without creating pet.json, or a partial bundle download is missing the manifest.
Related errors
- pet.json is invalid.
- pet.json must not be a symlink.
- pet.json exceeded the manifest size limit.
- Could not read the selected path.
- Could not read the selected file.
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/b0b2c488c185cbaa.
Report an issue: GitHub.