{"record":{"id":"b04f872eb68b31b6","repo":"stablyai/orca","slug":"animation-name-declares-anim-framedurations","errorCode":null,"errorMessage":"Animation \"${name}\" declares ${anim.frameDurationsMs.length} frame durations but ${anim.frames} frames.","messagePattern":"Animation \"(.+?)\" declares (.+?) frame durations but (.+?) frames\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/main/ipc/pet.ts","lineNumber":358,"sourceCode":"      if (dims.width % fw !== 0 || dims.height % fh !== 0) {\n        throw new Error(\n          `Spritesheet ${dims.width}×${dims.height} is not a clean multiple of frame ${fw}×${fh}.`\n        )\n      }\n      const columns = dims.width / fw\n      const rows = dims.height / fh\n      if (manifest.animations) {\n        for (const [name, anim] of Object.entries(manifest.animations)) {\n          if (anim.row >= rows) {\n            throw new Error(`Animation \"${name}\" references row ${anim.row} but sheet has ${rows}.`)\n          }\n          if (anim.frames > columns) {\n            throw new Error(\n              `Animation \"${name}\" has ${anim.frames} frames but sheet only has ${columns} columns.`\n            )\n          }\n          if (anim.frameDurationsMs && anim.frameDurationsMs.length !== anim.frames) {\n            throw new Error(\n              `Animation \"${name}\" declares ${anim.frameDurationsMs.length} frame durations but ${anim.frames} frames.`\n            )\n          }\n        }\n        if (manifest.defaultAnimation && !manifest.animations[manifest.defaultAnimation]) {\n          throw new Error(`defaultAnimation \"${manifest.defaultAnimation}\" not in animations.`)\n        }\n      }\n      sprite = {\n        frameWidth: fw,\n        frameHeight: fh,\n        columns,\n        rows,\n        sheetWidth: dims.width,\n        sheetHeight: dims.height,\n        fps: manifest.fps ?? 8,\n        defaultAnimation: manifest.defaultAnimation,\n        animations: manifest.animations","sourceCodeStart":340,"sourceCodeEnd":376,"githubUrl":"https://github.com/stablyai/orca/blob/1136503c6a231a16dce8f921f6fadb63d181e8db/src/main/ipc/pet.ts#L340-L376","documentation":"Thrown at pet.ts:357-360 when an animation provides frameDurationsMs (optional per pet.ts:107) but its array length does not equal anim.frames. Each frame must have exactly one duration, so a mismatch would either drop frames or index out of range during playback.","triggerScenarios":"An animation sets frames: 6 but frameDurationsMs: [100, 200, 300] (3 entries). Runs only when frameDurationsMs is present (line 357 guards with &&).","commonSituations":"Author added/removed a frame but forgot to update the durations array; durations were copied from a template with a different frame count.","solutions":["Make frameDurationsMs have exactly anim.frames entries (one duration per frame).","If you do not need per-frame timing, omit frameDurationsMs and rely on fps.","Re-export the bundle after fixing pet.json."],"exampleFix":"// before — 6 frames, 3 durations\n{ \"animations\": { \"idle\": { \"row\": 0, \"frames\": 6, \"frameDurationsMs\": [100,200,300] } } }\n// after\n{ \"animations\": { \"idle\": { \"row\": 0, \"frames\": 6, \"frameDurationsMs\": [100,100,100,100,100,100] } } }","handlingStrategy":"validation","validationCode":"function validateFrameDurations(animations: Record<string, {frames:number;frameDurationsMs?:number[]}>) {\n  for (const [name, a] of Object.entries(animations)) {\n    if (a.frameDurationsMs && a.frameDurationsMs.length !== a.frames) {\n      throw new Error(`animation ${name}: ${a.frameDurationsMs.length} durations vs ${a.frames} frames`)\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try { await importPetBundle(p) }\ncatch (e) { if (e instanceof Error && /frame durations but/.test(e.message)) { /* align durations array to frame count */ } else throw e }","preventionTips":["If you do not need per-frame timing, omit frameDurationsMs entirely.","When adding/removing a frame, update frameDurationsMs in the same edit."],"tags":["pet-bundle","animation","validation","timing"],"backgroundTag":null,"analyzedSha":"1136503c6a231a16dce8f921f6fadb63d181e8db","analyzedAt":"2026-08-12T23:15:58.167Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}