Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

Terrain draws tiles at sea level after a `raster` layer on the same raster-dem tileset is evicted — the tile-texture pool is keyed by size only (R32F DEM textures receive pooled RGBA8 textures)

Open
#13,736 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Assessment

Difficulty
3/5
Estimated time
1-2 days
Newbie friendliness
66/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
typescript

Research direction

Start by reading Painter.saveTileTexture and getTileTexture, then trace the DEM path through prepareDEMTexture and the raster path through Tile.setTexture and RasterTileSource.unloadTile. Reproduce with terrain and a raster layer on the same tileset, then verify the pool does not give a DEM an incompatible texture and that terrain retains its heights after raster tiles are evicted.

Written by the indexing model from the issue text.

Description

auto-triaged bug :lady_beetle:
mapbox-gl-js version

v3.18.1 (bundle read; no related entry in the changelog through 3.32)

Browser and version

Chrome 154 / Windows 11 / AMD Radeon 860M (D3D11); Chromium 145 / Linux (ANGLE D3D12). Likely all.

Expected behavior

Terrain tiles keep their heights.

Actual behavior

Console: WebGL: INVALID_OPERATION: texSubImage2D: type UNSIGNED_BYTE but ArrayBufferView not Uint8Array or Uint8ClampedArray (one per affected tile), and the affected terrain tiles are drawn flat at ~0 m: a slab far below its neighbours, the sky visible through the gap, the neighbours' skirts standing as walls. It persists until the terrain is rebuilt or the page reloaded.

Cause (from the bundle)

  • Painter.saveTileTexture / getTileTexture pool freed tile textures by texture.size[0] only.
  • The DEM tiles of this tileset are 512 px with a 1-px border: DEMData.stride = 514. prepareDEMTexture takes painter.getTileTexture(dem.stride) and otherwise creates a 514×514 R32F texture.
  • The same images decoded by a raster source become 514×514 RGBA8 textures (Tile.setTexture → getTileTexture(img.width)), which RasterTileSource.unloadTile returns to the pool.
  • The next DEM tile pops one of those; Texture.update issues texSubImage2D(…, RGBA, UNSIGNED_BYTE, Float32Array); the upload is rejected and the texture keeps the raster image, which the terrain shader reads as heights.

Same family as #13491 (raster-array textures reaching _prepareDEMTextures, Safari's wording of the same error).

Suggested fix

Key the pool by format as well as size (or never hand a pooled texture to prepareDEMTexture unless its format === gl.R32F).

Workaround we ship

Never pool textures of the DEM stride:

const orig = map.painter.saveTileTexture.bind(map.painter);
map.painter.saveTileTexture = (t) => { if (t?.size?.[0] === 514) return; orig(t); };
Link to the demonstration

No response

Steps to trigger the unexpected behavior
  1. A map with 3D terrain on mapbox://mapbox.mapbox-terrain-dem-v1, plus a raster layer on the same tileset (e.g. to colour the terrain by height with raster-color).
  2. Move around until raster tiles are evicted from the tile cache (set maxTileCacheSize: 0 to see it at once).
  3. Load new terrain tiles (pan to a new area).
<script>
mapboxgl.accessToken = '…';
const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/outdoors-v12', center: [137.70, 36.67], zoom: 10.9, pitch: 49, maxTileCacheSize: 0 });
map.on('style.load', () => {
  map.addSource('mapbox-dem', { type: 'raster-dem', url: 'mapbox://mapbox.mapbox-terrain-dem-v1', tileSize: 512, maxzoom: 14 });
  map.setTerrain({ source: 'mapbox-dem', exaggeration: 1 });
  map.addSource('dem-as-image', { type: 'raster', url: 'mapbox://mapbox.mapbox-terrain-dem-v1', tileSize: 2048, maxzoom: 14 });
  map.addLayer({ id: 'dem-as-image', type: 'raster', source: 'dem-as-image', paint: { 'raster-opacity': 0.5 } });
});
// then: map.jumpTo({ center: [140.70, 42.85] }) and back a few times
</script>
Relevant log output
WebGL: INVALID_OPERATION: texSubImage2D: type UNSIGNED_BYTE but ArrayBufferView not Uint8Array or Uint8ClampedArray
(one line per affected terrain tile — 16 in the session that showed it)
Dominant language
TypeScript
Stars
12.5k
Forks
2.4k
PR merge metrics
No merged PRs in 30d

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from mapbox/mapbox-gl-js

All issues in mapbox/mapbox-gl-js

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.