stablyai/orca · warning · Error
pet.json is invalid.
Error message
pet.json is invalid.
What it means
Thrown by the pet:importPetBundle handler when stat(manifestPath) succeeds but either manifestStat.isFile() is false (pet.json is a directory/device) or manifestStat.size already exceeds MAX_MANIFEST_BYTES (64 KiB, defined as 64 * 1024). The size pre-check is an early guard; a second TOCTOU check at line 276 re-verifies after reading. pet.json is spec'd to be tiny, so an oversized manifest is treated as malicious/stuffed.
Source
Thrown at src/main/ipc/pet.ts:265
}
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'}`)
}
// Why: spritesheetPath is bundle-relative and attacker-controlled — reject absolute/escaping paths (and symlinks) so a bundle can't reach outside.View on GitHub (pinned to 1136503c6a)
Solutions
- Keep pet.json small — reference spritesheetPath rather than embedding image bytes.
- Confirm pet.json is a regular file, not a directory or device node.
- Trim oversized animations maps and long description fields (description is capped at 2000 chars by schema anyway).
- If you genuinely need a large manifest, that exceeds the format — restructure so binary assets live as separate files.
Defensive patterns
Strategy: validation
Validate before calling
import { stat } from 'node:fs/promises'
import { join } from 'node:path'
const MAX_MANIFEST_BYTES = 64 * 1024
const s = await stat(join(bundleDir, 'pet.json'))
if (!s.isFile() || s.size > MAX_MANIFEST_BYTES) {
notify('pet.json is invalid or too large (max 64 KiB).')
return
} Try / catch
try {
await ipcRenderer.invoke('pet:importPetBundle')
} catch (e) {
if (e instanceof Error && e.message === 'pet.json is invalid.') {
notify('pet.json is the wrong type or exceeds 64 KiB. Reference external spritesheetPath instead of embedding data.')
} else throw e
} Prevention
- Keep pet.json tiny — never embed image bytes; use spritesheetPath.
- Confirm pet.json is a regular file, not a directory.
- Trim large animations maps before exporting the bundle.
When it happens
Trigger: pet.json is actually a directory (e.g. the user named a folder 'pet.json'), or pet.json is larger than 64 KiB due to embedded base64, a huge animations map, or padding/junk appended to the file.
Common situations: A bundle generator embedded a base64-encoded spritesheet inside pet.json instead of referencing spritesheetPath; a hand-edited manifest grew past 64 KiB; or the manifest was corrupted/padded.
Related errors
- pet.json exceeded the manifest size limit.
- Bundle is missing pet.json.
- pet.json must not be a symlink.
- File is too large (${(srcStat.size / (1024 * 1024)).toFixed(
- Could not read the selected path.
AI-assisted analysis of stablyai/orca@1136503c6a (2026-08-12).
Data as JSON: /api/errors/6d54aee592caaeb6.
Report an issue: GitHub.