{"record":{"id":"fb1181e8a64fa647","repo":"Hmbown/CodeWhale","slug":"whale-point-asset-is-unavailable","errorCode":null,"errorMessage":"Whale point asset is unavailable.","messagePattern":"Whale point asset is unavailable\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"pet/src/ui/pet.ts","lineNumber":278,"sourceCode":"    liveTape.reset();\n    if (!document.hidden && world && !restoring) { world.resumeObservation(); seconds = world.frame.timeMs / 1000; }\n  }\n  void persist(); if (audio) anchor = audio.currentTime - seconds + .08;\n});\nwindow.addEventListener('pagehide', () => { void persist(); });\nfunction animate(now: number) {\n  if (world && !restoring && !paused && !document.hidden) {\n    accumulator += last ? Math.min(.1, (now - last) / 1000) : 0;\n    while (accumulator >= 1 / 30 && (mode.value === 'live' || !tape.length || seconds < duration())) {\n      world.step(1 / 30, { motion: !motion.checked, sensitivity: 1 }); seconds = world.frame.timeMs / 1000; play(world.voices); accumulator -= 1 / 30;\n    }\n    if ((mode.value === 'live' || !tape.length) && seconds >= duration()) seek.max = String(Math.ceil(seconds / 30) * 30 + 30);\n    draw();\n  }\n  last = now; requestAnimationFrame(animate);\n}\ntry {\n  const response = await fetch('./whale-points.tsv'); if (!response.ok) throw new Error('Whale point asset is unavailable.');\n  points = (await response.text()).trim().split('\\n').map(row => row.trim().split(/\\s+/).map(Number) as [number, number]);\n  if (points.length !== 980 || points.some(p => p.length !== 2 || !p.every(Number.isFinite))) throw new Error('Invalid whale point asset.');\n  try {\n    const saved = await library.getHabitat();\n    if (saved) {\n      savedRevision = saved.revision; const h = saved.habitat;\n      if (h.petPersistenceVersion !== 1 || !Number.isFinite(h.seconds) || h.seconds < 0 || h.seconds > PET_MAX_SECONDS\n        || !['wild', 'demo', 'replay'].includes(h.source) || typeof h.still !== 'boolean' || typeof h.sourceName !== 'string') throw new Error('Saved habitat is invalid. Save a replay file before replacing it.');\n      expressionVersion = h.expressionVersion === undefined ? 1 : h.expressionVersion;\n      if (![1, 2].includes(expressionVersion) || h.checkpoint && (h.checkpoint.sim?.expressionVersion ?? 1) !== expressionVersion) throw new Error('Saved expression version does not match its checkpoint.');\n      const savedWorld = PetWorld.fromRecording(points, { ...h, petReplayVersion: h.petReplayVersion === undefined ? 1 : h.petReplayVersion });\n      if (h.checkpoint && savedWorld.frame.timeMs / 1000 !== h.seconds) throw new Error('Saved pet clock does not match its checkpoint.');\n      tape = h.tape; interactions = [...h.interactions]; mode.value = h.source; motion.checked = h.still || media.matches;\n      source.textContent = h.sourceName;\n      seek.max = String(Math.max(h.source === 'demo' ? 80 : h.source === 'replay' ? 90 : 120, savedWorld.endTimeMs / 1000));\n      if (h.source === 'replay') {\n        mode.querySelector<HTMLOptionElement>('[value=\"replay\"]')!.disabled = false;\n      }","sourceCodeStart":260,"sourceCodeEnd":296,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/433685b2024e7bc4c99e1e2e326bcad39b4d9d65/pet/src/ui/pet.ts#L260-L296","documentation":"Thrown at startup when `fetch('./whale-points.tsv')` returns a non-ok HTTP status. The pet UI requires this asset to build the whale point cloud and cannot start the habitat without it, so the fetch failure is converted into this explicit error inside the startup try block.","triggerScenarios":"Opening pet.ts when `./whale-points.tsv` is missing from the served directory, the server returns 404/500, or a service worker/offline cache serves an error response.","commonSituations":"Deploying the UI without copying static assets, wrong base path / reverse proxy rewrite, offline PWA with uncached asset, dev server serving only bundled JS.","solutions":["Ensure `whale-points.tsv` exists next to the page and is served with a 200 status.","Check the network tab for the actual status/URL of `./whale-points.tsv` and fix the deployment path.","If using a service worker, add the asset to the precache list."],"exampleFix":"// before: asset missing at ./whale-points.tsv\n// after: copy it into the deploy output\ncp assets/whale-points.tsv dist/whale-points.tsv","handlingStrategy":"try-catch","validationCode":"const head = await fetch('./whale-points.tsv', { method: 'HEAD' });\nif (!head.ok) showFatal('whale-points.tsv is not deployed.');","typeGuard":"null","tryCatchPattern":"try { await loadWhalePoints(); } catch (e) { showFatal('Pet assets failed to load: ' + e.message); }","preventionTips":["Include whale-points.tsv in the deploy/build output checklist.","Add the asset to the service-worker precache list.","Smoke-test the deployed URL for a 200 before release."],"tags":["fetch","http","asset"],"backgroundTag":"http-error-response","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"}