mapbox-gl-js icon indicating copy to clipboard operation
mapbox-gl-js copied to clipboard

SourceEXpressionBinder.updatePaintArray this.expression.evaluate is not a function

Open stevage opened this issue 2 years ago • 2 comments

I'm running into this nasty intermittent exception that gets thrown. I can't reliably reproduce it, and I can't even imagine how I'd go about setting up a minimal repro case, but I'm reporting it here so that the next person who hits it at least has something to google for.

mapbox-gl-js version: 2.13.0

browser: Chrome

Steps to Trigger Behavior

It's a pretty complex React app with quite a few layers. When you click on features in a certain layer, it updates the style to highlight the thing you clicked on etc etc. There's also feature-state updating on hover.

Maybe 1 time in 100 when you click, an exception is thrown:

mapbox-gl-dev.js:15439 Uncaught TypeError: this.expression.evaluate is not a function
    at SourceExpressionBinder.updatePaintArray (mapbox-gl-dev.js:15439:1)
    at ProgramConfiguration.updatePaintArrays (mapbox-gl-dev.js:15714:1)
    at ProgramConfigurationSet.updatePaintArrays (mapbox-gl-dev.js:15848:1)
    at FillBucket.update (mapbox-gl-dev.js:28111:1)
    at Tile.setFeatureState (mapbox-gl-dev.js:40275:1)
    at SourceFeatureState.initializeTileState (mapbox-gl-dev.js:40646:1)
    at SourceCache._tileLoaded (mapbox-gl-dev.js:42994:1)
    at VectorTileSource.done (mapbox-gl-dev.js:50296:1)
    at Actor.processTask (mapbox-gl-dev.js:39056:1)

Digging into the code a little bit, updatePaintArray is being called on a thing whose expression is { kind: 'constant, value: { r:0, g:0, b: 0, a: 1}. Which doesn't have an evaluate function, so...exception.

image

That doesn't really help me know what to do about it though.

This full call stack suggests that the trigger ultimately was a call to removeLayer() but maybe I'm misinterpreting that.

image

Any thoughts? Ideas? Workarounds? Simply suppressing the exception would be a pretty good outcome.

stevage avatar Jul 26 '23 13:07 stevage

Weirdly I'm hitting this again in a completely different app, Vue based. It happens when the user is changing stuff while the map is still repainting from the last change they made.

This time the stack trace:

image

stevage avatar Dec 05 '24 01:12 stevage

I'm having the same issue with our Vue-based app. It works the first time it renders, we then leave the page and return back to it and get these errors:

Uncaught TypeError: this.expression.evaluate is not a function
    at Ro.updatePaintArray (program_configuration.ts:222:39)
    at program_configuration.ts:528:53
    at yo.eachPosition (feature_position_map.ts:50:16)
    at Fo.updatePaintArrays (program_configuration.ts:526:32)
    at Oo.updatePaintArrays (program_configuration.ts:686:69)
    at Oc.update (fill_bucket.ts:143:36)
    at _t.updateBuckets (tile.ts:629:20)
    at _t.refreshFeatureState (tile.ts:603:14)
    at mt.initializeTileState (source_state.ts:118:14)
    at gt._tileLoaded (source_cache.ts:292:21)

In addition, the map no longer renders properly (this is meant to be the streets style): Image

We are doing a lot of unregistering to ensure the map doesn't leave anything around (including randomising the ID of the map container), but still it's a problem. We've also tried downgrading to v3.8.0, but it's still failing.

Our destruction routine when the page is left:

{
  beforeDestroy() {
    this.cleanupMap();
  },
  methods: {
    if (this.map) {
        this.map.off();
        this.map.remove();
        this.map = null;
        this.initialised = false;
      }
  }
}

This is how we're initiating the map:

{
  data () {
    return {
      map: null,
      initialised: false,
      mapContainerId: `map_${new Date().getTime()}`, // Unique ID for each instance
    }
  },
  mounted() {
    this.initializeMap();
  },
  methods: {
    async initializeMap() {
      window.mapboxgl.accessToken = "xyz";

      this.map = new window.mapboxgl.Map({
        container: this.mapContainerId,
        //style: 'mapbox://styles/mapbox/streets-v12',
        center: mapCentre,
        zoom: 3,
        minZoom: 3,
        bearing: 26.4,
        pitch: 54,
        projection: 'mercator',
        preserveDrawingBuffer: true,
        antialias: true
      });

      await Promise.all([
        new Promise((resolve) => {
          this.map.once('style.load', () => {
            console.log('Style loaded');
            resolve();
          });
        }),
        new Promise((resolve) => {
          this.map.once('load', () => {
            console.log('Map loaded');
            this.map.resize();
            resolve();
          });
        }),
      ]);

      this.map.addControl(new window.mapboxgl.FullscreenControl());
      this.map.addControl(new window.mapboxgl.NavigationControl({
        showCompass: true,
        showZoom: true,
        visualizePitch: true,
      }));

      this.initialised = true;
      this.map.on('error', (e) => {
        console.error('Mapbox GL error:', e);
      });

      this.map.on('styledata', (e) => {
        console.log('Style data updated:', e);
      });
    }
  }
}

Help! :)

tommed avatar Feb 20 '25 11:02 tommed