Skip to content

map.removeSource(...) throws TypeError in Terrain.update under globe projection on styles without terrain #13665

Description

@colinmeinke

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):

  1. Call terrain.recalculate(...) in _createTerrain so properties is initialized synchronously.
  2. 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:

  1. projection: 'globe'.
  2. Style without a terrain block (mapbox/streets-v12 in the repro).
  3. 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)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions