Hmbown/CodeWhale · error · Error
Invalid Engine batch.
Error message
Invalid Engine batch.
What it means
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.
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.
Example fix
// before
bridge.observeEngineBatch(JSON.stringify(allPendingEvents)); // 200 events: throws
// after
for (let i = 0; i < allPendingEvents.length; i += 64) {
bridge.observeEngineBatch(JSON.stringify(allPendingEvents.slice(i, i + 64)), timeMs);
} Defensive patterns
Strategy: validation
Validate before calling
function canObserveBatch(metadataJSON) {
try {
const v = JSON.parse(metadataJSON);
return Array.isArray(v) && v.length <= 64;
} catch { return false; }
} Type guard
const isEngineBatch = (v) => Array.isArray(v) && v.length >= 0 && v.length <= 64;
Try / catch
try {
bridge.observeEngineBatch(json, timeMs);
} catch (e) {
if (e.message === 'Invalid Engine batch.') {
for (const ev of JSON.parse(json)) bridge.observeEngine(JSON.stringify(ev), timeMs);
} else throw e;
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Invalid Engine batch.
- 1
- A pinned task provider requires an explicit model
- A positive pull request number is required
- A provider and page loader are required.
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/01140656a1a6e6d5.
Report an issue: GitHub.
Appendix: source
Thrown at pet/ios/Resources/pet-native.js:97
this.liveTape.reset();
this.segment = undefined;
this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000);
this.engine = new pet_engine_js_1.PetEngineTelemetry();
}
/** Resume a live host at the first unrecorded bucket. Keep every accepted
* interval, but never present its last observed frame as current evidence. */
resumeEngine() {
this.world.resumeObservation();
this.engineTick = Math.round(this.world.frame.timeMs * 30 / 1000);
this.engine = new pet_engine_js_1.PetEngineTelemetry();
this.world.voices = [];
return this.world.frame.timeMs;
}
observeEngine(metadataJSON, timeMs) { this.engine.observe(JSON.parse(metadataJSON), timeMs); }
observeEngineBatch(metadataJSON, timeMs) {
const events = JSON.parse(metadataJSON);
if (!Array.isArray(events) || events.length > 64)
throw new Error('Invalid Engine batch.');
const next = this.engine.clone();
for (const event of events)
next.observe(event, timeMs);
this.engine = next;
}
advanceEngine(timeMs, motion, waiting) {
const target = Math.floor(timeMs * 30 / 1000 + 1e-8);
if (!Number.isFinite(timeMs) || target < this.engineTick || target - this.engineTick > 300)
throw new Error('Engine pet clock jump.');
this.engine.confirmWaiting(timeMs, waiting);
const voices = [];
while (this.engineTick < target) {
if ((this.engineTick + 1) % 12 === 0)
this.world.acceptTelemetry(this.engine.bucket(Math.floor(this.engineTick / 12)));
this.world.step(1 / 30, { motion, sensitivity: 1 });
voices.push(...this.world.voices);
this.engineTick++;
}View on GitHub (pinned to 433685b202)