Repository: maplibre/maplibre-gl-js (2025-08-17T17:02:43Z)

Issue:
Enabling terrain during animation causes map to freeze
<!--
Hello! Thanks for contributing.  For the fastest response and resolution, please:

 - Make the issue title a succinct but specific description of the unexpected behavior.
   Bad: "Map rotation is broken". 
   Good: "map.setBearing(...) throws a TypeError for negative values"

 - Include a link to a minimal demonstration of the bug – more below …

 - Ensure you can reproduce the bug using the latest release.

 - Check the console for relevant errors and warnings

 - Only post to report a bug. For feature requests, please use https://github.com/maplibre/maplibre-gl-js/issues/new?template=Feature_request.md instead.  Direct all other questions to https://stackoverflow.com/questions/tagged/maplibre-gl-js

-->

**maplibre-gl-js version**: Reproduced in 3.6.2, 4.7.1, 5.6.0

**browser**: Firefox, Chrome

### Steps to Trigger Behavior

 1. Set up a map instance without terrain
 2. Start a map animation, for example using `easeTo`
 3. Enable terrain while the animation is running

### Link to Demonstration

https://jsbin.com/barilagose/edit?html,console,output

### Expected Behavior

- The animation should finish
- Terrain should be enabled
- The map should be usable after the animation finishes

### Actual Behavior

- The map freezes and stops updating - controls are still usable though
- The map instance is no longer usable
- Every attempt at a map interaction causes an unhandled exception (`'Attempting to run(), but is already running.'`)


Discussion:
We have some nasty issues when it comes to moving the map and changing its state, this is probably one of them.
Feel free to dig into it and fix that...
I've found a way to avoid this crash in #6031.
@HarelM , what issues are you referring to? Is it general observation, or are there some specific tickets with repros that you can link me?
The following is the one that I remembered, but I think there were others: 
- #3328



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 783c5a4f4c5..eae2e8b66b9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
 ### 🐞 Bug fixes
 - Prevent original input style JSON from being mutated by `Style.set*` methods ([#6216](https://github.com/maplibre/maplibre-gl-js/pull/6216))
 - Fix evaluating `global-state` in paint properties with other subexpressions ([#6048](https://github.com/maplibre/maplibre-gl-js/issues/6048))
+- Fix enabling terrain while transitioning [#6011](https://github.com/maplibre/maplibre-gl-js/issues/6011)
 
 ## 5.6.2
 
diff --git a/src/ui/camera.ts b/src/ui/camera.ts
index 4dbd31850d1..18bec5e578a 100644
--- a/src/ui/camera.ts
+++ b/src/ui/camera.ts
@@ -1187,6 +1187,11 @@ export abstract class Camera extends Evented {
     }
 
     _updateElevation(k: number) {
+
+        if (this._elevationStart === undefined || this._elevationCenter === undefined) {
+            this._prepareElevation(this.transform.center);
+        }
+
         this.transform.setMinElevationForCurrentTile(this.terrain.getMinTileElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom));
         const elevation = this.terrain.getElevationForLngLatZoom(this._elevationCenter, this.transform.tileZoom);
         // target terrain updated during flight, slowly move camera to new height


Test patch:
diff --git a/src/ui/camera.test.ts b/src/ui/camera.test.ts
index 62296299942..ec1f4d6b712 100644
--- a/src/ui/camera.test.ts
+++ b/src/ui/camera.test.ts
@@ -1258,6 +1258,27 @@ describe('easeTo', () => {
 
         expect(spy.mock.calls.find(c => 'done' in c[0])).toBeTruthy();
     });
+
+    test('terrain set during easeTo', () => {
+        const camera = createCamera();
+        const stubNow = vi.spyOn(browser, 'now');
+
+        stubNow.mockImplementation(() => 0);
+
+        camera.easeTo({bearing: 97, duration: 500});
+        
+        stubNow.mockImplementation(() => 100);
+        camera.simulateFrame();
+
+        const terrain = {getMinTileElevationForLngLatZoom: () => 0,
+            getElevationForLngLatZoom: () => 0};
+        camera.terrain = terrain as any;
+
+        stubNow.mockImplementation(() => 500);
+        camera.simulateFrame();
+
+        expect(camera.getBearing()).toEqual(97);
+    });
 });
 
 describe('flyTo', () => {