{"record":{"id":"d91f6973d8ea130b","repo":"Hmbown/CodeWhale","slug":"invalid-pet-random-stream-sim","errorCode":null,"errorMessage":"Invalid pet random stream.","messagePattern":"Invalid pet random stream\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"pet/src/core/pet-sim.ts","lineNumber":97,"sourceCode":"    throw new Error('Invalid pet state.');\n}\n\nconst hex2rgb = (h: string) => [parseInt(h.slice(1, 3), 16), parseInt(h.slice(3, 5), 16), parseInt(h.slice(5, 7), 16)];\nconst RGB = CHANNELS.map(c => hex2rgb(c.color));\nconst UNKNOWN_RGB = hex2rgb('#738492');\nconst REST_RGB = [122, 214, 240];\n\n// mulberry32 — a 32-bit seeded PRNG tiny enough to port by hand correctly.\nexport function mulberry32(seed: number) {\n  let a = seed >>> 0;\n  return Object.assign(() => {\n    a = (a + 0x6D2B79F5) >>> 0;\n    let t = a;\n    t = Math.imul(t ^ (t >>> 15), t | 1);\n    t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n    return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n  }, { state: () => a, restore: (state: number) => {\n    if (!Number.isSafeInteger(state) || state < 0 || state > 0xffffffff) throw new Error('Invalid pet random stream.');\n    a = state;\n  } });\n}\n\nexport interface Particle {\n  x: number; y: number; vx: number; vy: number;\n  s: number; jx: number; jy: number; pod: number;\n  hx: number; hy: number; ang: number; rad: number; tail: number;\n  tx: number; ty: number;\n}\n\nexport interface Frame {\n  r: number; g: number; b: number;   // particle colour, 0..255\n  alpha: number;                     // uniform per-dot alpha\n  hollow: boolean;                   // coverage gap: rings, not filled dots\n  channel: string;                   // active category\n  arch: string;                      // active archetype\n  work: number;                      // rest↔work blend actually applied","sourceCodeStart":79,"sourceCodeEnd":115,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/core/pet-sim.ts#L79-L115","documentation":"The mulberry32 PRNG's restore(state) accepts only a 32-bit unsigned integer: it must be a safe integer in [0, 0xffffffff]. Any other value throws 'Invalid pet random stream.' This keeps the pet's deterministic simulation reproducible — a corrupted RNG state would silently desync frames and scores.","triggerScenarios":"Calling restore() with a float (e.g. saved after arithmetic that left 0.5), a negative number, a value > 4294967295, or a non-number (string/undefined) — often from a hand-written or truncated checkpoint containing the RNG state.","commonSituations":"Restoring RNG state from JSON where it was serialized as a string; saving state before the >>>0 normalization; computing state via division instead of bit ops; mixing two PRNG streams' states.","solutions":["Coerce before restore: state = Math.floor(Number(state)) >>> 0, then validate range","Persist the RNG state exactly as state() returns it, without further arithmetic or string conversion","If the saved value fails validation, restart the stream from a known seed instead of restoring a corrupted one","Verify the value's provenance — only feed back output of the stream's own state() accessor"],"exampleFix":"// before\nrng.restore(saved.state);\n// after\nconst s = Math.floor(Number(saved.state));\nif (Number.isSafeInteger(s) && s >= 0 && s <= 0xffffffff) rng.restore(s >>> 0);\nelse rng = mulberry32(DEFAULT_SEED);","handlingStrategy":"validation","validationCode":"function canRestoreRngState(state) {\n  return Number.isSafeInteger(state) && state >= 0 && state <= 0xffffffff;\n}","typeGuard":"const isRngState = (v: unknown): v is number =>\n  typeof v === 'number' && Number.isSafeInteger(v) && v >= 0 && v <= 0xffffffff;","tryCatchPattern":"try {\n  rng.restore(saved);\n} catch (err) {\n  if (err.message === 'Invalid pet random stream.') rng = mulberry32(DEFAULT_SEED);\n  else throw err;\n}","preventionTips":["Persist rng.state() output verbatim; never post-arithmetic values","Keep RNG state in a dedicated, type-checked field of the checkpoint","Fall back to a fixed seed when saved state is invalid to keep determinism"],"tags":["prng","determinism","validation"],"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-22T11:17:16.035Z"}