mapbox-gl-js version
v3.22.0
Browser and version
Chromium and Safari
Expected behavior
map.removeSource(id) should not throw on a globe-projection map whose style has no terrain block.
Actual behavior
removeSource throws TypeError: Cannot read properties of undefined (reading 'get') synchronously, originating in Terrain.update. Reproduces on v3.22.0; the relevant code paths are unchanged on v3.23.0-rc.2, so the bug should reproduce there too.
Root cause
style.terrain (instance of src/style/terrain.ts) declares a properties field that is uninitialized in the constructor and only assigned by recalculate(). Style._createTerrain doesn't call recalculate, so style.terrain.properties is undefined until the first Style.update(parameters) runs from Map._render.
Map.removeSource calls _updateTerrain synchronously with no intervening render, so it can land in that window. Terrain.update then dereferences terrainProps.get('source') (terrain/terrain.ts:388) and throws.
Globe-only because setTerrainForDraping() runs only when projection.requiresDraping, so mercator leaves style.terrain null and the early-return in Painter.updateTerrain catches it.
Suggested fixes (either, or both):
- Call
terrain.recalculate(...) in _createTerrain so properties is initialized synchronously.
- Guard
Terrain.update:
- if (style && style.terrain) {
+ if (style && style.terrain && style.terrain.properties) {
Link to the demonstration
https://codepen.io/editor/colinmeinke/pen/019dd931-4bde-7956-901a-72ecb0d913bc
Steps to trigger the unexpected behavior
Opening the pen is enough. It fires on style.load with no user interaction.
Conditions:
projection: 'globe'.
- Style without a terrain block (
mapbox/streets-v12 in the repro).
removeSource called before the first _render frame after style load.
Relevant log output
TypeError: Cannot read properties of undefined (reading 'get')
at Terrain.update (terrain/terrain.ts:388)
at Painter.updateTerrain (render/painter.ts:429)
at Map.\_updateTerrain (ui/map.ts:4892)
at Map.removeSource (ui/map.ts:2603)
mapbox-gl-js version
v3.22.0
Browser and version
Chromium and Safari
Expected behavior
map.removeSource(id)should not throw on a globe-projection map whose style has no terrain block.Actual behavior
removeSourcethrowsTypeError: Cannot read properties of undefined (reading 'get')synchronously, originating inTerrain.update. Reproduces on v3.22.0; the relevant code paths are unchanged on v3.23.0-rc.2, so the bug should reproduce there too.Root cause
style.terrain(instance ofsrc/style/terrain.ts) declares apropertiesfield that is uninitialized in the constructor and only assigned byrecalculate().Style._createTerraindoesn't callrecalculate, sostyle.terrain.propertiesisundefineduntil the firstStyle.update(parameters)runs fromMap._render.Map.removeSourcecalls_updateTerrainsynchronously with no intervening render, so it can land in that window.Terrain.updatethen dereferencesterrainProps.get('source')(terrain/terrain.ts:388) and throws.Globe-only because
setTerrainForDraping()runs only whenprojection.requiresDraping, so mercator leavesstyle.terrainnull and the early-return inPainter.updateTerraincatches it.Suggested fixes (either, or both):
terrain.recalculate(...)in_createTerrainsopropertiesis initialized synchronously.Terrain.update:Link to the demonstration
https://codepen.io/editor/colinmeinke/pen/019dd931-4bde-7956-901a-72ecb0d913bc
Steps to trigger the unexpected behavior
Opening the pen is enough. It fires on
style.loadwith no user interaction.Conditions:
projection: 'globe'.mapbox/streets-v12in the repro).removeSourcecalled before the first_renderframe after style load.Relevant log output