{"record":{"id":"d6c6ed13cc170f04","repo":"Hmbown/CodeWhale","slug":"invalid-pet-raster-size-d6c6ed","errorCode":null,"errorMessage":"Invalid pet raster size.","messagePattern":"Invalid pet raster size\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-native.ts","lineNumber":99,"sourceCode":"    const next = this.engine.clone();\n    for (const event of events) next.observe(event, timeMs);\n    this.engine = next;\n  }\n  advanceEngine(timeMs: number, motion: boolean, waiting: boolean): void {\n    const target = Math.floor(timeMs * 30 / 1000 + 1e-8);\n    if (!Number.isFinite(timeMs) || target < this.engineTick || target - this.engineTick > 300) throw new Error('Engine pet clock jump.');\n    this.engine.confirmWaiting(timeMs, waiting);\n    const voices: PetVoice[] = [];\n    while (this.engineTick < target) {\n      if ((this.engineTick + 1) % 12 === 0) 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    }\n    this.world.voices = voices;\n  }\n  terminal(width: number, height: number): string {\n    if (![width, height].every(n => Number.isSafeInteger(n) && n >= 1 && n <= 512)) throw new Error('Invalid pet raster size.');\n    const { sim, frame } = this.world, l = layout(width * 2, height * 4, frame.state);\n    const cells = Array(width * height).fill(0), bits = [[1, 8], [2, 16], [4, 32], [64, 128]];\n    sim.p.forEach((p, i) => {\n      if (frame.state.observed < .92 && i % 2 === 1) return;\n      const x = Math.round(l.ox + p.x * l.scale * l.flipX), y = Math.round(l.oy + p.y * l.scale);\n      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];\n    });\n    return JSON.stringify({ width, height, cells, timeMs: frame.timeMs, channel: frame.state.channel, arch: ARCH_OF[frame.state.channel],\n      hollow: frame.state.observed < .92, dozing: frame.behaviour === 'doze', lit: frame.state.lit });\n  }\n  pcmChannels(voicesJSON: string, startSample: number, length: number, rate: number): [Float32Array, Float32Array] {\n    const p = renderPetPCM(JSON.parse(voicesJSON) as PetVoice[], startSample, length, rate);\n    return [p.left, p.right];\n  }\n  pcm(voicesJSON: string, startSample: number, length: number, rate: number): string {\n    return JSON.stringify(this.pcmChannels(voicesJSON, startSample, length, rate).map(channel => Array.from(channel)));\n  }\n}","sourceCodeStart":81,"sourceCodeEnd":117,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-native.ts#L81-L117","documentation":"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.","triggerScenarios":"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.","commonSituations":"Layout code measuring a hidden container as 0; passing floats from resize observers; forgetting the 512 hard cap when rendering a very large window.","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"],"exampleFix":"// before\nconst art = pet.terminal(measuredWidth, measuredHeight);\n// after\nconst w = Math.floor(measuredWidth), h = Math.floor(measuredHeight);\nconst art = (w >= 1 && w <= 512 && h >= 1 && h <= 512) ? pet.terminal(w, h) : null;","handlingStrategy":"validation","validationCode":"function canRasterize(w, h) {\n  return [w, h].every(n => Number.isSafeInteger(n) && n >= 1 && n <= 512);\n}","typeGuard":null,"tryCatchPattern":"try {\n  art = pet.terminal(w, h);\n} catch (err) {\n  if (err.message === 'Invalid pet raster size.') art = pet.terminal(clampInt(w,1,512), clampInt(h,1,512));\n  else throw err;\n}","preventionTips":["Floor and clamp dimensions from resize observers / CSS measurements","Skip rendering while the container has zero size","Remember the 512-per-axis hard cap"],"tags":["raster","bounds-validation","rendering"],"backgroundTag":"argument-out-of-range","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"}