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

Can't catch all useGLTF errors with React Error Boundaries #3181

Open
StephenHaney opened this issue Feb 24, 2024 · 7 comments
Open

Can't catch all useGLTF errors with React Error Boundaries #3181

StephenHaney opened this issue Feb 24, 2024 · 7 comments
Labels
bug Something isn't working

Comments

@StephenHaney
Copy link

StephenHaney commented Feb 24, 2024

I've noticed that some useGLTF errors don't get caught by the error boundary (since react error boundaries only handle errors that impact the react tree).

If you cause the decoding to fail, by say deleting random bytes out of a glb file, something inside useGLTF will throw but not be caught by the error boundary. This just causes the app to crash and a blank canvas. I'm running into this on a high traffic site where a small percentage of network requests for my glb file get interrupted.

Any advice on how to handle cases like these?

@StephenHaney
Copy link
Author

StephenHaney commented Feb 24, 2024

Here is my workaround code for now in case it is helpful for folks.

Note: this isn't idiomatic React and probably causes other problems in some situations.

export const BambooBox = () => {
  try {
    const { nodes, materials } = useGLTF(path);

    return (
        <mesh
          geometry={nodes.Cylinder.geometry}
          material={materials.Bamboo}
        />
    );
  } catch (err) {
    return <Fallback />;
  }
};

@CodyJasonBennett
Copy link
Member

I'd need to see an example, but this sounds like a React bug which should be taken upstream. You can harden your workaround by continuing to throw a promise if passed to err so Suspense works as expected.

@StephenHaney
Copy link
Author

I think it's expected behavior of react error boundaries that they won't catch errors in async code – my understanding is that the error needs to happen in the React tree's render stack to catch it.

Here's a repro (I deleted random info in the GLB file to simulate a bad network request)
https://codesandbox.io/p/sandbox/dazzling-lederberg-8dhfcv

You'll see that even though there's an error boundary, the error thrown by useGLTF is causing the entire app to crash.

Seems odd that I'd be the first person to find this so I could definitely be missing something... although you might only notice
if you're paying careful attention within large amounts of traffic.

@CodyJasonBennett
Copy link
Member

Okay, we're probably not catching errors thrown from promises. We don't intend for this tricky behavior to be possible.

@StephenHaney
Copy link
Author

The way it happened in the wild was a valid URL but a network error on Poki (so not a 404 but not the full file returned either) happening once every thousand users or so.

@CodyJasonBennett CodyJasonBennett added the bug Something isn't working label Mar 25, 2024
@CodyJasonBennett
Copy link
Member

CodyJasonBennett commented Mar 25, 2024

I think it's expected behavior of react error boundaries that they won't catch errors in async code – my understanding is that the error needs to happen in the React tree's render stack to catch it.

Looks like that's exactly the problem here. You can get the same broken behavior with below (where it skips the error boundary entirely), but not if you make load synchronous.

import * as React from 'react'
import * as THREE from 'three'
import { createRoot } from 'react-dom/client'
import { ErrorBoundary } from 'react-error-boundary'
import { useLoader, Canvas } from '@react-three/fiber'

class ThrowableLoader extends THREE.Loader {
  async load() {
    throw 'test'
  }
}

function Test() {
  useLoader(ThrowableLoader, '')
}

createRoot(root).render(
  <ErrorBoundary fallback={<div>Something went wrong</div>}>
    <Canvas>
      <Test />
    </Canvas>
  </ErrorBoundary>,
)

@CodyJasonBennett
Copy link
Member

@drcmda, we should take this upstream with suspend-react, but is this a known issue or pitfall with legacy Suspense?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants