Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rendering stops with "TypeError: Cannot read properties of undefined (reading 'rectangles')" or "TypeError: node is undefined" #11769

Closed
ethanchristensen01 opened this issue Jan 17, 2024 · 10 comments

Comments

@ethanchristensen01
Copy link

ethanchristensen01 commented Jan 17, 2024

What happened?

Cesium sometimes unexpectedly crashes with this message when I pan the map or zoom with my mouse. It started when I tried migrating my project from vue 2 + webpack to vue 3 + vite.

Firefox stack trace:

An error occurred while rendering.  Rendering has stopped.
TypeError: node is undefined
    findMaxLevelFromNode@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173294:24
    findMaxLevelFromNode@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173259:11
    TileAvailability.prototype.computeMaximumLevelAtPosition@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173054:10
    TileAvailability.prototype.isTileAvailable@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173111:15
    CesiumTerrainProvider.prototype.getTileDataAvailable@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:176157:26
    GlobeSurfaceTileProvider.prototype.canRefine@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:206614:47
    visitTile2@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:209462:20
    visitIfVisible@http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:209729:12
    visitVisibleChildrenNearToFar@…

Chrome stack trace:

An error occurred while rendering.  Rendering has stopped.
TypeError: Cannot read properties of undefined (reading 'rectangles')
TypeError: Cannot read properties of undefined (reading 'rectangles')
    at findMaxLevelFromNode (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173294:29)
    at findMaxLevelFromNode (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173259:11)
    at TileAvailability.computeMaximumLevelAtPosition (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173054:10)
    at TileAvailability.isTileAvailable (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:173111:15)
    at CesiumTerrainProvider.getTileDataAvailable (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:176157:26)
    at prepareNewTile (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:197834:35)
    at GlobeSurfaceTile.initialize (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:197669:5)
    at GlobeSurfaceTile.processStateMachine (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:197674:20)
    at GlobeSurfaceTileProvider.loadTile (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:206444:28)
    at processSinglePriorityLoadQueue (http://localhost:8080/node_modules/.vite/deps/cesium.js?v=a0fc6289:209851:18)

Reproduction steps

This bug is difficult to reproduce. It might be something I'm doing wrong with my codebase, but it's a little too big to pinpoint what exactly it could be right now. I do know we call sampleTerrainMostDetailed quite a bit.

The relevant section of the cesium source code is here.

// Work up the tree until we find a rectangle that contains this point.
while (node !== stopNode) {
const rectangles = node.rectangles;
// Rectangles are sorted by level, lowest first.
for (
let i = rectangles.length - 1;
i >= 0 && rectangles[i].level > maxLevel;
--i
) {
const rectangle = rectangles[i];
if (rectangleContainsPosition(rectangle, position)) {
maxLevel = rectangle.level;
}
}
node = node.parent;
}

It looks like it's trying to traverse up a tree via node.parent while node !== stopNode (i.e. until node === stopNode), but it never checks if node is undefined. If node is undefined, that most likely means we already reached the root node. But why are we reaching the root node before stopNode?

Does that mean the program is in a broken state? If so, there should be a specific error message explaining how the invariants have been invalidated and what might have caused it.

Is node !== stopNode the right comparison? We're comparing references here, not the contents of the object. If this is the problem, maybe we should implement a Node.isEquals method or something.

If the comparison is correct and the state is valid, maybe a simple undefined check would fix this.

Sandcastle example

No response

Environment

Browser: Chrome 120.0.6099.217 and Firefox 121.0.1
CesiumJS Version: 1.106.1 and 1.112.0
Operating System: Windows 10
Dev Server: Vite 5.0.6

@ethanchristensen01
Copy link
Author

For what it's worth, this bug seems to happen primarily with the Cesium World Terrain. When I use locally hosted terrain, this doesn't happen even if I get the occasional 404 error for a terrain tile at a certain level.

@ggetz
Copy link
Contributor

ggetz commented Jan 19, 2024

@ethanchristensen01 Could you please provide your build configuration from before and after?

@jjspace Can you please take a look at this given that it seems to arise when switching from webpack to vite?

@ggetz ggetz added the needs feedback On hold until additional info is supplied label Jan 19, 2024
@ethanchristensen01
Copy link
Author

ethanchristensen01 commented Jan 24, 2024

@ggetz @jjspace Here is a gist containing the old and new config files

At some point, we tried using vite-plugin-cesium instead of vite-plugin-static-copy, but this error was still occurring.

@ethanchristensen01
Copy link
Author

I've updated the gist I shared earlier to include a file which creates the viewer like how I'm doing it. It's an unusual way of doing it, so that might be worth checking out.

@ggetz ggetz removed the needs feedback On hold until additional info is supplied label Jan 24, 2024
@ggetz
Copy link
Contributor

ggetz commented Jan 25, 2024

@ethanchristensen01 My guess is something when wrong with the terrain tile request.

Our recommended vite configuration has some additional options which may be factor.

@jjspace Can you take a look at this given the vite tooling?

@ethanchristensen01
Copy link
Author

@ggetz I've updated the vite config to reflect the recommended configuration, thank you for the pointer there. Changing that didn't seem to fix the bug.

I did some more testing and found that having a lot of billboard entities (pins) in one location made the error happen more consistently. Switching from a secondary terrain provider to Ion will usually trigger the error.

These pins are supposed to be clamped to the ground. Their positions also get updated every few seconds, during which we might be getting the altitude of the terrain at the new position with sampleTerrainMostDetailed.

Tomorrow, I will test more with our old build to see if I can consistently break it there, too.

@jjspace
Copy link
Contributor

jjspace commented Jan 29, 2024

@ethanchristensen01 Sorry updating the config didn't work. It sounds like this is possibly not be a vite/build issue? If you can consistently reproduce it in your project are you able to recreate it in a minimal Sandcastle example?

@ethanchristensen01
Copy link
Author

@jjspace I've been troubleshooting this when I've had the time to.

I narrowed down a single function where the error would consistently happen. This function accepts a viewer parameter and a second parameter, extracts coordinates from the second parameter, and calls sampleTerrainMostDetailed(viewer.terrainProvider, coordinates). It runs about 4 times per second.

After trying almost everything I could think of, I tried logging out viewer in this function. It logged a Viewer wrapped with an es6 Proxy. Vue 3 wraps components' data members with Proxy to handle reactivity, so this was a sign that my function was getting called with a viewer instance defined in a Vue component. Unwrapping it with Vue's toRaw function fixed my error.

HYPOTHESIS: Calling sampleTerrainMostDetailed using a viewer wrapped with a reactive Proxy (i.e. Vue 3 reactive) can cause problems if the terrain updates while sampling the terrain. This is more likely to happen if the terrain is hosted remotely, as the terrain might not load as fast. Defining a viewer/a reference to a viewer in a Vue component should be avoided.

Unfortunately it's a little hard to set this up in Sandcastle.

@ethanchristensen01
Copy link
Author

ethanchristensen01 commented Feb 12, 2024

@ggetz With a better understanding of what happened in this issue, I don't think it's possible to prevent this from happening on the Cesium side. Developers will have to know not to put the Cesium viewer in a Proxy. You can decide to leave this issue open or closed, since my problem is solved. Creating some kind of warning in the Vue 3 cesium example would help future developers, so maybe an issue could be created for that. There actually isn't a readily available example for using cesium with Vue 3, so if we do want to give a warning, it'd have to go somewhere else.

I created an issue in the Vue 3 migration guide repo vuejs/v3-migration-guide#63 as well. This requests documentation be added to warn users about async race conditions when making complex class instances reactive.

@jjspace
Copy link
Contributor

jjspace commented Feb 14, 2024

@ethanchristensen01 I'm definitely glad you found a solution to the issue! That said it sounds like this is an issue outside of Cesium itself and, as you said, I'm not sure we can do much about it.

Vue 3 wraps components' data members with Proxy to handle reactivity, so this was a sign that my function was getting called with a viewer instance defined in a Vue component. Unwrapping it with Vue's toRaw function fixed my error.

This is really helpful to know and we'll definitely keep it in mind if we see more people with similar issues. Thanks for sharing your investigations!

I'm going to close this for now as there's no further action at this time. If we see this reported often we may add some documentation somewhere but that can be assessed in the future.

@jjspace jjspace closed this as completed Feb 14, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants