{"record":{"id":"01140656a1a6e6d5","repo":"Hmbown/CodeWhale","slug":"invalid-engine-batch-pet-native","errorCode":null,"errorMessage":"Invalid Engine batch.","messagePattern":"Invalid Engine batch\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/ios/Resources/pet-native.js","lineNumber":97,"sourceCode":"        this.liveTape.reset();\n        this.segment = undefined;\n        this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000);\n        this.engine = new pet_engine_js_1.PetEngineTelemetry();\n    }\n    /** Resume a live host at the first unrecorded bucket. Keep every accepted\n     * interval, but never present its last observed frame as current evidence. */\n    resumeEngine() {\n        this.world.resumeObservation();\n        this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000);\n        this.engine = new pet_engine_js_1.PetEngineTelemetry();\n        this.world.voices = [];\n        return this.world.frame.timeMs;\n    }\n    observeEngine(metadataJSON, timeMs) { this.engine.observe(JSON.parse(metadataJSON), timeMs); }\n    observeEngineBatch(metadataJSON, timeMs) {\n        const events = JSON.parse(metadataJSON);\n        if (!Array.isArray(events) || events.length > 64)\n            throw new Error('Invalid Engine batch.');\n        const next = this.engine.clone();\n        for (const event of events)\n            next.observe(event, timeMs);\n        this.engine = next;\n    }\n    advanceEngine(timeMs, motion, waiting) {\n        const target = Math.floor(timeMs * 30 / 1000 + 1e-8);\n        if (!Number.isFinite(timeMs) || target < this.engineTick || target - this.engineTick > 300)\n            throw new Error('Engine pet clock jump.');\n        this.engine.confirmWaiting(timeMs, waiting);\n        const voices = [];\n        while (this.engineTick < target) {\n            if ((this.engineTick + 1) % 12 === 0)\n                this.world.acceptTelemetry(this.engine.bucket(Math.floor(this.engineTick / 12)));\n            this.world.step(1 / 30, { motion, sensitivity: 1 });\n            voices.push(...this.world.voices);\n            this.engineTick++;\n        }","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/ios/Resources/pet-native.js#L79-L115","documentation":"observeEngineBatch() applies a batch of engine metadata observations atomically: it validates the parsed JSON is an array of at most 64 events, clones the engine, applies each event to the clone, and only then swaps it in. A non-array payload or a batch larger than 64 events is rejected as an invalid batch.","triggerScenarios":"observeEngineBatch(metadataJSON, timeMs) called with JSON that parses to an object or scalar instead of an array, or an array with more than 64 events (e.g. flushing a long queue of pending observations in one call).","commonSituations":"Buffering engine events and flushing without chunking; a producer changing the payload shape from array to {events: [...]} after an API update; passing the raw metadata object instead of a JSON array of events.","solutions":["Chunk pending observations into batches of at most 64 events before calling observeEngineBatch().","Ensure the producer emits a JSON array of events, not a wrapped object.","Fall back to calling observeEngine() per event if you cannot control batch size.","Validate Array.isArray(events) && events.length <= 64 on the caller side before invoking."],"exampleFix":"// before\nbridge.observeEngineBatch(JSON.stringify(allPendingEvents)); // 200 events: throws\n// after\nfor (let i = 0; i < allPendingEvents.length; i += 64) {\n  bridge.observeEngineBatch(JSON.stringify(allPendingEvents.slice(i, i + 64)), timeMs);\n}","handlingStrategy":"validation","validationCode":"function canObserveBatch(metadataJSON) {\n  try {\n    const v = JSON.parse(metadataJSON);\n    return Array.isArray(v) && v.length <= 64;\n  } catch { return false; }\n}","typeGuard":"const isEngineBatch = (v) => Array.isArray(v) && v.length >= 0 && v.length <= 64;","tryCatchPattern":"try {\n  bridge.observeEngineBatch(json, timeMs);\n} catch (e) {\n  if (e.message === 'Invalid Engine batch.') {\n    for (const ev of JSON.parse(json)) bridge.observeEngine(JSON.stringify(ev), timeMs);\n  } else throw e;\n}","preventionTips":["Chunk observation queues to batches of at most 64 before flushing.","Keep the producer's payload shape as a plain JSON array of events.","Add a round-trip test asserting the batch contract between producer and bridge."],"tags":["validation","batch","engine"],"backgroundTag":"invalid-argument-value","analyzedSha":"433685b2024e7bc4c99e1e2e326bcad39b4d9d65","analyzedAt":"2026-09-15T12:24:24.634Z","contentChangedAt":"2026-09-15T12:24:24.634Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}