drei icon indicating copy to clipboard operation
drei copied to clipboard

useTexture crashes if image is not valid

Open stingray21 opened this issue 1 year ago • 2 comments

  • three version: 0.161.0
  • @react-three/fiber version: 8.15.16
  • @react-three/drei version: 9.97.6
  • node version: 18.19.1
  • npm version: 10.2.4

Problem description:

I'm using @react-three/fiber's useTexture to generate a decal for a three.js object.

The url for the image file (fileName) gets generated dynamically and the image files live on a different server

This works fine as long as the file exists. However, I get a runtime error, if the file is not valid.

Unhandled Runtime Error
Error: Could not load https://example.com/files/image: undefined

I cannot figure out how to catch/mitigate that error.

Relevant code:

decal = useTexture(fileName)

Here is a minimal working code sample: https://codesandbox.io/p/sandbox/adoring-dijkstra-mz2hsy

Suggested solution:

I already tried to wrap it in a try/catch

  let decal = {}
  try {
    decal = useTexture(fileName)
  } catch (error) {
    console.log('Could not load texture', fileName)
  }

but that never loads the decal, even if the image file exists and causes a never ending amount of this error:

Uncaught TypeError: Cannot read properties of undefined (reading 'elements')
    at Matrix3.copy (three.module.js:1425:22)
    at refreshTransformUniform (three.module.js:17783:23)
    at refreshUniformsCommon (three.module.js:17847:13)
    at Object.refreshMaterialUniforms (three.module.js:17806:13)
    at setProgram (three.module.js:19469:27)
    at WebGLRenderer.renderBufferDirect (three.module.js:18762:29)
    at renderObject (three.module.js:19201:23)
    at renderObjects (three.module.js:19183:21)
    at renderScene (three.module.js:19101:48)
    at WebGLRenderer.render (three.module.js:19005:17)
    at render$1 (index-8afac004.esm.js:1506:63)
    at loop (index-8afac004.esm.js:1528:27)
    at sentryWrapped (helpers.js:80:23)

And this version (using an empty image in catch)

  let decal = {}
  try {
    decal = useTexture(fileName)
  } catch (error) {
    console.log('Could not load texture', fileName)
decal = useTexture('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=')
  }

causes this error:

Unhandled Runtime Error
Error: Should have a queue. This is likely a bug in React. Please file an issue.

Seems like this drei issue is related, but I couldn't fix my problem that way (ErrorBoundary) either.

When I use TextureLoader instead of useTexture, i.e.

// decal = useTexture(fileName)
decal = new THREE.TextureLoader().load(fileName)

then there is just no texture with the faulty url and I get this error in the console:

console.js:43 THREE.WebGLRenderer: Texture marked for update but no image data found.

but the site does not crash completely.

stingray21 avatar Mar 18 '24 19:03 stingray21

A library like this might help: https://github.com/bvaughn/react-error-boundary

nikolajbech avatar May 08 '24 14:05 nikolajbech

You can't put hooks in try/catch blocks (https://github.com/facebook/react/issues/16026) so I wouldn't expect useTexture to do anything about that, but as ^ implied you can catch it with ErrorBoundary (nice lib here) and attempt to pass a fallback texture or do whatever else you imagined inside catch before resetting the EB render.

zr00da avatar Sep 17 '24 04:09 zr00da