{"record":{"id":"55096fc25cfbe1c2","repo":"louis-e/arnis","slug":"no-preview-data-available","errorCode":null,"errorMessage":"No preview data available","messagePattern":"No preview data available","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/gui/js/preview3d.js","lineNumber":199,"sourceCode":"        px[i++] = whole % 256;\n        px[i++] = Math.floor((e - whole) * 256);\n        px[i++] = 255;\n      }\n    }\n    return img;\n  }\n\n  // The terrain and hillshade sources request every tile with the same URL;\n  // caching the synthesized ImageData halves the per-tile sampling work.\n  const tileCache = new Map();\n  const TILE_CACHE_MAX = 48;\n\n  function ensureProtocol() {\n    if (protocolRegistered) return;\n    maplibregl.addProtocol(PROTOCOL, async (params) => {\n      const m = params.url.match(/^arnisdem:\\/\\/(\\d+)\\/(\\d+)\\/(\\d+)\\/(\\d+)/);\n      const d = m && datasets.get(+m[1]);\n      if (!d) throw new Error(\"No preview data available\");\n      let img = tileCache.get(params.url);\n      if (!img) {\n        img = renderDemTile(d, +m[2], +m[3], +m[4]);\n        tileCache.set(params.url, img);\n        if (tileCache.size > TILE_CACHE_MAX) {\n          tileCache.delete(tileCache.keys().next().value);\n        }\n      }\n      return { data: await createImageBitmap(img) };\n    });\n    protocolRegistered = true;\n  }\n\n  function landCoverUrl(grid, gw, gh) {\n    const canvas = document.createElement(\"canvas\");\n    canvas.width = gw;\n    canvas.height = gh;\n    const ctx = canvas.getContext(\"2d\");","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/louis-e/arnis/blob/34048924d9365795fb0d832e76140a3fbdc413d9/src/gui/js/preview3d.js#L181-L217","documentation":"ensureProtocol registers a custom maplibregl protocol handler for arnisdem:// URLs; when maplibre requests a tile, the handler parses the dataset id from the URL and looks it up in the module-local datasets Map. If no dataset with that id is registered (or the regex doesn't match), the handler rejects with this Error, surfacing as a failed tile request in the map.","triggerScenarios":"A maplibre source with tiles under arnisdem://<datasetId>/... is still attached to a live map after the dataset was removed from the datasets Map (e.g. preview regenerated, selection cleared, or module state reset), or the URL does not match the /^arnisdem:\\/\\/(\\d+)\\/(\\d+)\\/(\\d+)\\/(\\d+)/ pattern (e.g. non-numeric id), so `d` is undefined.","commonSituations":"Stale DEM tile requests from a previous map instance racing with a regenerated preview; a cached map style JSON referencing an old dataset id; calling createTerrainMap before the dataset was added to the registry; hot module reload clearing the module-level Map while the map keeps requesting tiles.","solutions":["Ensure the dataset is registered via datasets.set(id, d) before adding/using any arnisdem:// source, and keep the id consistent with the tile URLs in the source.","Remove the map source and detach the map when the dataset is deleted/regenerated instead of leaving stale arnisdem:// URLs live.","Catch the rejection in the protocol handler flow (maplibre surfaces it as a source error event); listen to map.on('error') and ignore/suppress errors for known-stale dataset ids.","If ids are derived from user input, validate they are numeric before building arnisdem:// URLs.","Clear tileCache and rebuild the source with the new dataset id whenever the preview is regenerated."],"exampleFix":"// before\nmap.setTerrain({ source: \"dem\", ... }); // dem source still uses old arnisdem://7/... id after regen\n// after\nmap.on(\"error\", (e) => {\n  if (String(e.error && e.error.message).includes(\"No preview data available\")) return; // stale tile\n  console.error(e);\n});\n// and on regeneration:\nif (map.getSource(\"dem\")) map.removeSource(\"dem\");\naddDemSource(map, newDatasetId);","handlingStrategy":"try-catch","validationCode":"const ds = typeof datasetId === \"number\" && Number.isInteger(datasetId) ? datasetId : null;\nif (ds == null || !datasetRegistryHas(ds)) { skipMapSetup(); return; }","typeGuard":"function hasDataset(id) { return Number.isInteger(id) && datasets.has(id); }","tryCatchPattern":"map.on(\"error\", (e) => {\n  if (e && e.error && e.error.message === \"No preview data available\") return; // stale tile for old dataset\n  console.error(\"map error:\", e);\n});","preventionTips":["Register the dataset before adding any arnisdem:// source to the map.","Remove map sources/terrain when a dataset is deleted or regenerated.","Validate dataset ids are numeric before embedding them in tile URLs.","Add a map 'error' listener that tolerates stale-tile failures."],"tags":["maplibre","tauri","protocol-handler","stale-state"],"backgroundTag":"missing-dataset-reference","analyzedSha":"34048924d9365795fb0d832e76140a3fbdc413d9","analyzedAt":"2026-09-03T14:05:17.283Z","contentChangedAt":"2026-09-03T14:05:17.283Z","schemaVersion":2},"datasetVersion":"2026-09-10T17:17:09.494Z"}