Hmbown/CodeWhale · error · Error
Invalid pet raster size.
Error message
Invalid pet raster size.
What it means
terminal(width, height) rasterizes the pet into a character grid and validates dimensions up front: width and height must be safe integers in [1, 512]. Anything else (0, negative, fractional, >512, NaN, non-number) throws 'Invalid pet raster size.' This bounds the cell buffer at width*height before rendering.
Solutions
- Clamp dimensions before calling: Math.max(1, Math.min(512, Math.floor(w)))
- Skip the render when width/height are 0/NaN and re-render on the next resize
- Guard the resize handler until the terminal reports real integer dimensions
- Split oversized views into multiple <=512 rasters if a bigger image is needed
Example fix
// before const art = pet.terminal(measuredWidth, measuredHeight); // after const w = Math.floor(measuredWidth), h = Math.floor(measuredHeight); const art = (w >= 1 && w <= 512 && h >= 1 && h <= 512) ? pet.terminal(w, h) : null;
Defensive patterns
Strategy: validation
Validate before calling
function canRasterize(w, h) {
return [w, h].every(n => Number.isSafeInteger(n) && n >= 1 && n <= 512);
} Try / catch
try {
art = pet.terminal(w, h);
} catch (err) {
if (err.message === 'Invalid pet raster size.') art = pet.terminal(clampInt(w,1,512), clampInt(h,1,512));
else throw err;
} Prevention
- Floor and clamp dimensions from resize observers / CSS measurements
- Skip rendering while the container has zero size
- Remember the 512-per-axis hard cap
When it happens
Trigger: Calling terminal() with dimensions from unvalidated UI state (0-size pane while hidden, fractional CSS px values, DPI-scaled values like 80.5); passing terminal.cols/rows before they are initialized (NaN/0); requesting a raster larger than 512 in either axis.
Common situations: Layout code measuring a hidden container as 0; passing floats from resize observers; forgetting the 512 hard cap when rendering a very large window.
Related errors
AI-assisted analysis of Hmbown/CodeWhale@433685b202 (2026-09-15).
Data as JSON: /api/errors/d6c6ed13cc170f04.
Report an issue: GitHub.
Appendix: source
Thrown at pet/src/core/pet-native.ts:99
const next = this.engine.clone();
for (const event of events) next.observe(event, timeMs);
this.engine = next;
}
advanceEngine(timeMs: number, motion: boolean, waiting: boolean): void {
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: PetVoice[] = [];
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++;
}
this.world.voices = voices;
}
terminal(width: number, height: number): string {
if (![width, height].every(n => Number.isSafeInteger(n) && n >= 1 && n <= 512)) throw new Error('Invalid pet raster size.');
const { sim, frame } = this.world, l = layout(width * 2, height * 4, frame.state);
const cells = Array(width * height).fill(0), bits = [[1, 8], [2, 16], [4, 32], [64, 128]];
sim.p.forEach((p, i) => {
if (frame.state.observed < .92 && i % 2 === 1) return;
const x = Math.round(l.ox + p.x * l.scale * l.flipX), y = Math.round(l.oy + p.y * l.scale);
if (x >= 0 && x < width * 2 && y >= 0 && y < height * 4) cells[Math.floor(y / 4) * width + Math.floor(x / 2)] |= bits[y % 4][x % 2];
});
return JSON.stringify({ width, height, cells, timeMs: frame.timeMs, channel: frame.state.channel, arch: ARCH_OF[frame.state.channel],
hollow: frame.state.observed < .92, dozing: frame.behaviour === 'doze', lit: frame.state.lit });
}
pcmChannels(voicesJSON: string, startSample: number, length: number, rate: number): [Float32Array, Float32Array] {
const p = renderPetPCM(JSON.parse(voicesJSON) as PetVoice[], startSample, length, rate);
return [p.left, p.right];
}
pcm(voicesJSON: string, startSample: number, length: number, rate: number): string {
return JSON.stringify(this.pcmChannels(voicesJSON, startSample, length, rate).map(channel => Array.from(channel)));
}
}View on GitHub (pinned to 433685b202)