{"record":{"id":"9e3980483b66583f","repo":"Hmbown/CodeWhale","slug":"invalid-pet-export-cursor","errorCode":null,"errorMessage":"Invalid pet export cursor.","messagePattern":"Invalid pet export cursor\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/src/tui/pet_watch/pet-native.js","lineNumber":305,"sourceCode":"                if (committed || this.origin !== previous || this.tick < c.tick)\n                    throw new Error('The pet recording segment has changed.');\n                this.tapeLog.splice(0, dropTape);\n                this.interactionLog.splice(0, dropInputs);\n                this.bucketIndex -= dropTape;\n                this.interactionIndex -= dropInputs;\n                this.tapeHashes = [];\n                this.hashTape(0);\n                this.segmented = true;\n                this.origin = next.origin;\n                committed = true;\n            } };\n    }\n    /** Lossless version 1 export, including the current pose and score. Drivers\n     * consume all chunks synchronously on the world's owner before another tick.\n     * Only a small slice is serialized inside an embedded runtime at a time. */\n    recordingChunk(index, completed = false) {\n        if (!Number.isSafeInteger(index) || index < 0)\n            throw new Error('Invalid pet export cursor.');\n        const size = 16, tapeEnd = completed ? this.bucketIndex + 1 : this.tapeLog.length;\n        const inputEnd = completed ? this.interactionIndex : this.interactionLog.length;\n        const tapes = Math.ceil(tapeEnd / size), inputs = Math.ceil(inputEnd / size);\n        if (index === 0)\n            return `{\"petReplayVersion\":${this.segmented ? 2 : 1},\"expressionVersion\":${this.sim.expressionVersion},\"tape\":[`;\n        if (index <= tapes)\n            return (index === 1 ? '' : ',') + JSON.stringify(this.tapeLog.slice((index - 1) * size, Math.min(tapeEnd, index * size))).slice(1, -1);\n        if (index === tapes + 1)\n            return '],\"interactions\":[';\n        const part = index - tapes - 2;\n        if (part < inputs)\n            return (part === 0 ? '' : ',') + JSON.stringify(this.interactionLog.slice(part * size, Math.min(inputEnd, (part + 1) * size))).slice(1, -1);\n        if (part === inputs) {\n            const history = this.historyDigest(tapeEnd, inputEnd);\n            return `]${this.origin ? ',\"start\":' + JSON.stringify({ ...this.origin, hasTelemetry: this.hasTelemetry, history }) : ''},\"checkpoint\":${JSON.stringify({ ...this.checkpoint(), history })}}`;\n        }\n        return null;\n    }","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/src/tui/pet_watch/pet-native.js#L287-L323","documentation":"recordingChunk(index) exports a recording in fixed-size chunks addressed by a cursor. The index must be a safe non-negative integer; drivers consume chunks synchronously starting at 0. A negative, fractional, or out-of-IEEE-range index cannot address a chunk, so the export is aborted with this error.","triggerScenarios":"Calling `world.recordingChunk(index)` with a non-integer (e.g. 1.5), a negative number, a value beyond Number.MAX_SAFE_INTEGER, or NaN/Infinity.","commonSituations":"An off-by-one or decrement-below-zero loop over chunk indices; dividing to compute an index and getting a fraction; a driver passing an uninitialized/NaN cursor; continuing a chunk loop after completion returned and the cursor arithmetic went negative.","solutions":["Start the cursor at 0 and increment by 1 per chunk: `for (let i = 0; ; i++) { const chunk = w.recordingChunk(i); if (!chunk) break; }`.","Guard the index with Number.isSafeInteger(index) && index >= 0 before calling.","Round or floor any computed index before passing it in.","Fix the loop termination so the cursor never goes negative after the final chunk."],"exampleFix":"// before\nlet cursor = totalChunks; // or a computed fraction\nworld.recordingChunk(cursor--);\n// after\nfor (let i = 0; i < totalChunks; i++) {\n  const chunk = world.recordingChunk(i);\n  // consume chunk synchronously\n}","handlingStrategy":"validation","validationCode":"if (!Number.isSafeInteger(index) || index < 0)\n  throw new Error('recordingChunk index must be a safe non-negative integer');","typeGuard":"const isValidChunkIndex = (i) => Number.isSafeInteger(i) && i >= 0;","tryCatchPattern":"try {\n  const chunk = world.recordingChunk(i);\n} catch (err) {\n  if (err.message === 'Invalid pet export cursor.')\n    console.error('Bad export cursor', i);\n  else throw err;\n}","preventionTips":["Drive exports with a simple `for (let i = 0; ; i++)` loop that stops on the final chunk.","Floor any computed index; never pass fractions or NaN.","Clamp the cursor to >= 0 when looping backwards or retrying.","Consume chunks synchronously on the world's owner, as the API requires."],"tags":["export","argument-validation","pagination"],"backgroundTag":"invalid-argument-value","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}