Upgrading to cesium 1.92: handleTileFailure -> error undefined - Tile is unloaded before the content finishes loading

Hi,

I’ve just updated to Cesium 1.92, where when.js has been replaced with native promises.

I’m now receiving a large stack trace, the main error shown below:

Unhandled Promise rejection: Cannot read properties of undefined (reading 'message') ; Zone: <root> ; Task: Promise.then ; Value: TypeError: Cannot read properties of undefined (reading 'message')
    at node_modules\@cesiumgs\cesium-analytics\Build\CesiumUnminified\Cesium.js:225729:1
    at ZoneDelegate.invoke (zone.js:372:1)
    at Zone.run (zone.js:134:1)
    at zone.js:1276:1
    at ZoneDelegate.invokeTask (zone.js:406:1)
    at Zone.runTask (zone.js:178:1)
    at drainMicroTaskQueue (zone.js:582:1)
    at ZoneTask.invokeTask [as invoke] (zone.js:491:1)
    at invokeTask (zone.js:1600:1)
    at XMLHttpRequest.globalZoneAwareCallback (zone.js:1637:1) TypeError: Cannot read properties of undefined 

This correlates to the below, from Cesium3DTileset.js:

function handleTileFailure(tileset, tile) {
  return function (error) {
    const url = tile._contentResource.url;
    const message = defined(error.message) ? error.message : error.toString();
    if (tileset.tileFailed.numberOfListeners > 0) {
      tileset.tileFailed.raiseEvent({
        url: url,
        message: message,
      });
    } else {
      console.log(`A 3D tile failed to load: ${url}`);
      console.log(`Error: ${message}`);
    }
  };
}

Where error is undefined.

This only happens when I navigate to a new route in an SPA, right after I zoom in on a tileset and you can see it is still loading.

Prior to navigating, the viewer is destroyed, and to my understanding primitives (and by extension tilesets) are also destroyed.

When interrogating the tileset from handleTileFailure, I can see that the tileset is not ready and several properties will return the error: “The tileset is not loaded. Use Cesium3DTileset.readyPromise or wait for Cesium3DTileset.ready to be true.”

From looking at the update method within Ceisum3DTileset.js, the requestTiles method is called if the tileset is ready, where requestContent is called which handles the errors for the tileset content ready promise handleTileFailure:

  tile.contentReadyPromise
    .then(handleTileSuccess(tileset, tile))
    .catch(handleTileFailure(tileset, tile));

contentReadyPromise is rejected within the following method:

function singleContentFailed(tile, tileset, error) {
  if (tile._contentState === Cesium3DTileContentState.PROCESSING) {
    --tileset.statistics.numberOfTilesProcessing;
  } else {
    --tileset.statistics.numberOfPendingRequests;
  }
  tile._contentState = Cesium3DTileContentState.FAILED;
  tile._contentReadyPromise.reject(error);
  tile._contentReadyToProcessPromise.reject(error);
}

At this point I can see:

tile.isDestroyed() == true
tile._contentState == Cesium3DTileContentState.FAILED;
error == undefined

singleContentFailed is called at line 1200 in Cesium3DTile.js, method requestSingleContent:

  promise
    .then(function (arrayBuffer) {
      if (tile.isDestroyed()) {
        // Tile is unloaded before the content finishes loading
        singleContentFailed(tile, tileset);
        return;
      }

singleContentFailed takes three arguments, (tile, tileset, error), and we can see error is missing here, causing the original error above where error is undefined.

So we can see that the promise is being reject due to: “Tile is unloaded before the content finishes loading”, where no error parameter is provided in the singleContentFailed.

Adding an error message removes most of the error messages, I now only receive:

Unhandled Promise rejection: Tile is unloaded before the content finishes loading ; 
Zone: <root> ; Task: Promise.then ; 
Value: Tile is unloaded before the content finishes loading undefined

...
Resource._Implementations.loadWithXhr	@	node_modules\@cesium…ied\Cesium.js:20200

I am using angular within zone.js, where zone.js wraps promises and catches any uncaught promises.

So it looks like somewhere there is a promise isn’t handling a rejection, looks like it has something to do with the deferred promise possibly.

Thanks,
Tom

Alright, looks to be tile.contentReadyToProcessPromise isn’t being caught anywhere, adding a catch solves the issue.

I’ve just seen a PR for the missing error messages in singleContentFailed. I’ll comment on this PR to add catch onto the above promise is this is closely related.

PR: fix Cesium3DTile singleContentFailed have no error by jiangheng90 · Pull Request #10289 · CesiumGS/cesium · GitHub

1 Like

Hi @TomPovey,

Thanks for the update - I am glad that you were able to resolve the issue through a workaorund.

Also, thanks for bumping this.

-Sam