SourceEXpressionBinder.updatePaintArray this.expression.evaluate is not a function
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.
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.
Any thoughts? Ideas? Workarounds? Simply suppressing the exception would be a pretty good outcome.
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:
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):
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! :)