{"record":{"id":"55474e08b205c602","repo":"Hmbown/CodeWhale","slug":"invalid-legacy-pet-recording-native","errorCode":null,"errorMessage":"Invalid legacy pet recording.","messagePattern":"Invalid legacy pet recording\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/ios/Resources/pet-native.js","lineNumber":258,"sourceCode":"        return { petReplayVersion: this.segmented ? 2 : 1, expressionVersion: this.sim.expressionVersion,\n            tape: this.tapeLog.slice(0, tapeEnd), interactions: this.interactionLog.slice(0, inputEnd).map(e => ({ ...e })),\n            ...(this.origin ? { start: { ...structuredClone(this.origin), hasTelemetry: this.hasTelemetry, history } } : {}),\n            ...(withCheckpoint ? { checkpoint: { ...this.checkpoint(), history } } : {}) };\n    }\n    static fromRecording(points, value) {\n        const r = value;\n        if (!r || ![1, 2].includes(r.petReplayVersion) || !Array.isArray(r.tape) || !Array.isArray(r.interactions))\n            throw new Error('Invalid pet recording.');\n        const version = r.expressionVersion === undefined ? 1 : r.expressionVersion;\n        if (![1, 2].includes(version))\n            throw new Error('Unsupported pet expression version.');\n        if (r.checkpoint !== undefined && !r.checkpoint || r.start !== undefined && !r.start)\n            throw new Error('Invalid pet checkpoint.');\n        for (const c of [r.start, r.checkpoint])\n            if (c && (c.sim?.expressionVersion ?? 1) !== version)\n                throw new Error('Pet expression version does not match its checkpoint.');\n        if (r.petReplayVersion === 1 && (r.start || r.checkpoint && r.checkpoint.petCheckpointVersion !== 1))\n            throw new Error('Invalid legacy pet recording.');\n        if (r.petReplayVersion === 2 && (!r.start || r.start.petCheckpointVersion !== 2 || r.start.historyStart !== r.start.tick))\n            throw new Error('The recording segment is missing its starting checkpoint.');\n        const first = r.start && PetWorld.restore(points, r.tape, r.interactions, r.start);\n        const world = r.checkpoint ? PetWorld.restore(points, r.tape, r.interactions, r.checkpoint) : first ?? new PetWorld(points, r.tape, r.interactions, version);\n        if (first) {\n            if (!world.segmented || world.tick < first.tick || r.checkpoint && r.checkpoint.historyStart !== first.tick)\n                throw new Error('Pet segment checkpoints do not agree.');\n            world.origin = first.checkpoint();\n        }\n        return world;\n    }\n    /** Retire only consumed input. The exact origin makes each archived segment\n     * independently replayable; no particle, random stream or score is reset. */\n    prepareSegment() {\n        const dropTape = Math.max(0, this.bucketIndex), dropInputs = this.interactionIndex, previous = this.origin;\n        const tape = this.tapeLog.slice(dropTape), interactions = this.interactionLog.slice(dropInputs);\n        const points = this.sim.p.map(p => [p.hx, p.hy]);\n        const c = this.checkpoint();","sourceCodeStart":240,"sourceCodeEnd":276,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/ios/Resources/pet-native.js#L240-L276","documentation":"Legacy (petReplayVersion 1) recordings must have no start checkpoint, and if a checkpoint is present it must have petCheckpointVersion 1. This error means a v1 recording carries v2-style segmented data (a start checkpoint, or a v2 checkpoint), which the legacy restore path cannot interpret.","triggerScenarios":"Passing a recording with petReplayVersion 1 that has a truthy r.start, or whose r.checkpoint.petCheckpointVersion !== 1.","commonSituations":"Downgrading the petReplayVersion field of a v2 segmented recording to 1 without stripping start/checkpoint; old app builds reading new saves whose fields got merged; manual data migration mistakes.","solutions":["Set petReplayVersion to 2 if the recording actually has a start checkpoint (i.e., it is a v2 segment).","Strip start and any v2 checkpoint to make it a genuine legacy recording, keeping only checkpoint with petCheckpointVersion 1.","Re-export from the current app so petReplayVersion matches the checkpoint structure.","Fix the exporter to derive petReplayVersion from the presence of a start checkpoint."],"exampleFix":"// before\nlegacy.petReplayVersion = 1; // but legacy.start exists\n// after\nlegacy.petReplayVersion = legacy.start ? 2 : 1;\nconst world = PetWorld.fromRecording(points, legacy);","handlingStrategy":"validation","validationCode":"if (r.petReplayVersion === 1 && (r.start || (r.checkpoint && r.checkpoint.petCheckpointVersion !== 1)))\n  throw new Error('legacy recording carries segmented data');","typeGuard":"const isConsistentLegacy = (r: any) =>\n  r.petReplayVersion !== 1 || (!r.start && (!r.checkpoint || r.checkpoint.petCheckpointVersion === 1));","tryCatchPattern":"try { world = PetWorld.fromRecording(points, r); }\ncatch (e) {\n  if (e.message === 'Invalid legacy pet recording.') {\n    r.petReplayVersion = r.start ? 2 : 1; // correct the version, retry\n    world = PetWorld.fromRecording(points, r);\n  } else throw e;\n}","preventionTips":["Derive petReplayVersion from the checkpoint structure at export time, never hardcode it.","Never downgrade petReplayVersion when migrating old saves without stripping v2 fields.","Keep a migration test that round-trips both v1 and v2 recordings."],"tags":["versioning","legacy","migration"],"backgroundTag":"schema-validation-failed","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-22T16:17:23.217Z"}