{"record":{"id":"e3e6b10026e6790c","repo":"Hmbown/CodeWhale","slug":"invalid-pet-raster-size-native","errorCode":null,"errorMessage":"Invalid pet raster size.","messagePattern":"Invalid pet raster size\\.","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/ios/Resources/pet-native.js","lineNumber":120,"sourceCode":"    }\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        }\n        this.world.voices = voices;\n    }\n    terminal(width, height) {\n        if (![width, height].every(n => Number.isSafeInteger(n) && n >= 1 && n <= 512))\n            throw new Error('Invalid pet raster size.');\n        const { sim, frame } = this.world, l = (0, pet_sim_js_1.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)\n                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)\n                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: pet_sim_js_1.ARCH_OF[frame.state.channel],\n            hollow: frame.state.observed < .92, dozing: frame.behaviour === 'doze', lit: frame.state.lit });\n    }\n    pcmChannels(voicesJSON, startSample, length, rate) {\n        const p = (0, pet_audio_js_1.renderPetPCM)(JSON.parse(voicesJSON), startSample, length, rate);\n        return [p.left, p.right];\n    }\n    pcm(voicesJSON, startSample, length, rate) {\n        return JSON.stringify(this.pcmChannels(voicesJSON, startSample, length, rate).map(channel => Array.from(channel)));","sourceCodeStart":102,"sourceCodeEnd":138,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/ios/Resources/pet-native.js#L102-L138","documentation":"terminal() renders the pet as a character-cell raster and validates that width and height are safe integers in [1, 512] before laying out a canvas of width*2 x height*4 pixels. Out-of-range or non-integer dimensions are rejected to prevent zero/negative or absurdly large allocations.","triggerScenarios":"terminal(width, height) called with 0, negative, non-integer (fractional from a resize calculation), NaN, or values above 512 (e.g. a full-screen size on a 4K terminal).","commonSituations":"Passing process.stdout.columns/rows before the first resize event (undefined -> NaN); floating-point terminal sizes from fractional cell metrics; very large terminal windows exceeding 512 columns/rows.","solutions":["Clamp width and height to integers in [1, 512] before calling terminal().","Compute dimensions as Math.max(1, Math.floor(value)) and handle undefined/NaN with a default (e.g. 80x24).","Cap against the 512 limit rather than passing raw terminal size.","Re-query terminal size on resize events instead of using cached values that may be undefined."],"exampleFix":"// before\nbridge.terminal(process.stdout.columns, process.stdout.rows); // undefined before first resize\n// after\nconst w = Math.min(512, Math.max(1, Math.floor(process.stdout.columns ?? 80)));\nconst h = Math.min(512, Math.max(1, Math.floor(process.stdout.rows ?? 24)));\nbridge.terminal(w, h);","handlingStrategy":"validation","validationCode":"function safeRasterSize(w, h) {\n  const c = (n, d) => Number.isSafeInteger(n) && n >= 1 && n <= 512 ? n : d;\n  return [c(Math.floor(w), 80), c(Math.floor(h), 24)];\n}\nconst [w, h] = safeRasterSize(cols, rows);","typeGuard":"const isValidRasterDim = (n) => Number.isSafeInteger(n) && n >= 1 && n <= 512;","tryCatchPattern":"try {\n  return bridge.terminal(w, h);\n} catch (e) {\n  if (e.message === 'Invalid pet raster size.') return bridge.terminal(80, 24);\n  throw e;\n}","preventionTips":["Default to 80x24 when terminal size is undefined (before the first resize event).","Always floor() and clamp to [1, 512]; never pass fractional cell metrics.","Recompute size on SIGWINCH/resize instead of caching possibly-undefined values."],"tags":["validation","rendering","argument-out-of-range"],"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"}