R3F GLTF Not Loading on AWS Amplify

I’m using webpack, distributing using AWS amplify.
Everything is working perfectly locally but as soon as it’s pushed to the server then I get an error that the GLTF file cannot be loaded, stating a different name than what the actual gltf file is called, is it renamed at some point during the loading process?

I figure most of these problems are attributed to me not having a public folder as amplify doesn’t support that, but the path for all the assets are correct and all of my other assets (pngs, svgs) that i’m animating using three.js are loading and working just fine, except the GLTF file.

Error message:

Uncaught Could not load /12aa89ff0220050381c1e9d62d3d9c14.gltf: Unexpected token < in JSON at position 0
react-reconciler.development.js:11782 The above error occurred in the <MercedesF1> component:

    at MercedesF1 (https://testing-gpracing.dk7zoarrp1yn8.amplifyapp.com/app.3fcb00d6.js:113650:62)
    at Suspense
    at group
    at Rig (https://testing-gpracing.dk7zoarrp1yn8.amplifyapp.com/app.3fcb00d6.js:60253:23)
    at Suspense
    at Suspense
    at ErrorBoundary (https://testing-gpracing.dk7zoarrp1yn8.amplifyapp.com/app.3fcb00d6.js:119466:5)
    at Provider (https://testing-gpracing.dk7zoarrp1yn8.amplifyapp.com/app.3fcb00d6.js:119745:3)

React will try to recreate this component tree from scratch using the error boundary you provided, ErrorBoundary.

The component that it is referencing:

/*
Auto-generated by: https://github.com/pmndrs/gltfjsx
*/
import * as THREE from 'three';
import React, { useRef } from 'react';
import { useGLTF } from '@react-three/drei';
import { GLTF } from 'three-stdlib';
import mercedes from '../../assets/f1.gltf';

// file is being resolved by loader and a new url is returned.
import { useLoader } from '@react-three/fiber';
// import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';

// const loader = new GLTFLoader();

type GLTFResult = GLTF & {
  nodes: {
    W05_me_body_SUB0_1: THREE.Mesh;
    W05_me_body_SUB0_2: THREE.Mesh;
    W05_me_body_SUB0_3: THREE.Mesh;
    W05_me_body_SUB0_4: THREE.Mesh;
    W05_me_body_SUB0_5: THREE.Mesh;
    W05_me_body_SUB0_6: THREE.Mesh;
    W05_me_body_SUB0_7: THREE.Mesh;
  };
  materials: {
    body: THREE.MeshStandardMaterial;
    mirror: THREE.MeshStandardMaterial;
    generic: THREE.MeshStandardMaterial;
    rim: THREE.MeshStandardMaterial;
    tyre: THREE.MeshStandardMaterial;
    glass: THREE.MeshStandardMaterial;
    Steer: THREE.MeshStandardMaterial;
  };
};

export default function MercedesF1({
  ...props
}: JSX.IntrinsicElements['group']) {
  const group = useRef<THREE.Group>();
  // const { nodes, materials } = useGLTF(mercedes) as GLTFResult;

  const { nodes, materials } = useLoader(GLTFLoader, mercedes);

  return (
    // <primitive object={scene} />
    <group ref={group} {...props} dispose={null}>
      <group rotation={[Math.PI / 2, 0, 0]}>
        <mesh
          castShadow
          receiveShadow
          //@ts-ignore
          geometry={nodes.W05_me_body_SUB0_1.geometry}
          material={materials.body}
        />
        <mesh
          castShadow
          receiveShadow
          //@ts-ignore
          geometry={nodes.W05_me_body_SUB0_2.geometry}
          material={materials.mirror}
        />
        <mesh
          castShadow
          receiveShadow
          //@ts-ignore
          geometry={nodes.W05_me_body_SUB0_3.geometry}
          material={materials.generic}
        />
        <mesh
          castShadow
          receiveShadow
          //@ts-ignore
          geometry={nodes.W05_me_body_SUB0_4.geometry}
          material={materials.rim}
        />
        <mesh
          castShadow
          receiveShadow
          //@ts-ignore
          geometry={nodes.W05_me_body_SUB0_5.geometry}
          material={materials.tyre}
        />
        <mesh
          castShadow
          receiveShadow
          //@ts-ignore
          geometry={nodes.W05_me_body_SUB0_6.geometry}
          material={materials.glass}
        />
        <mesh
          castShadow
          receiveShadow
          //@ts-ignore
          geometry={nodes.W05_me_body_SUB0_7.geometry}
          material={materials.Steer}
        />
      </group>
    </group>
  );
}

// const loadter = useGLTF.preload('../');

I’ve tried both useGLTF and useLoader in hopes that there is a difference in output but I get the same error both times. I’m completely stuck with this one. The gltf file is in my assets folder inside src, it’s copied across to the server as I can see it in the file directory. Not sure why the name is being changed from the f1.gltf to the random hash name given in the error, is it dumping this into a public folder that doesn’t exist?

Appreciate any help

I have the same bug :frowning:

wrong path, missing binares/texture. the error is a 404 error “file could not be loaded” which the GLTFparser tries to parse as a GLTF, thereby running into the < in JSON at position 0 thing. it happens if you guide GLTFLoader towards an url that it won’t be able to resolve.

in order to avoid issues, better use glb instead of gltf, they already include all assets and are compressed. i would stick models into /public and load them as “/model.glb”.

when you import a model it can in theory reside within /src, the bundler will now include it in the production build under /dist. it will not use the original name, it uses a hash signature in order to control caching the files.

there is no apparent error in your code, so i assume that the bundler lifts the gltf into /dist but not the textures and binaries that it refers to, and really there is no way it could or should. it doesn’t know your gltf refers to /textures/foo.png and /binaries/bar.bin or whatever, these files will be missing in prod. the solution is to use a glb.

Folks I found a solution that worked for me, same scenario with React, hosted in AWS Amplify using Three.js and GLTFLoader load function to load a model in my case named scene.gltf . Just changed the extension from gltf to json (scene.json), and that made the trick.

It seems the extension gltf is tricking the AWS Amplify server to resolve the path as some kind of HTML object, you can see this in the Developer Tools of your browser, going to the Network tab, if you try to access the gltf file that is in the public folder, directly with the URL like this: “https : // yourdomain.com / images / scene.gltf” you will see the browser tries to download a bunch of other files that seem unrelated with the gltf. That behaviour does not happen if you try to access a simple JPG file like this: “https://yourdomain.com/images/1.jpg” in which case only 1 file (1.jpg) is downloaded and presented on the screen. I also know that the GLTF file at the end is some kind of data in JSON format, and .JSON files are not threated as HTML but in its own object type “json”, I gave it a shot, changed the extension from scene.gltf to scene.json, published it with amplify into my AWS environment, ran it and worked like a charm.
I hope this helps! But it definitively needs some more digging from the ThreeJS team into why the GLTFLoader load function is not working in combination with React and hosted in AWS Amplify, only when the extension is .gltf and working when is .json Thanks!

The correct way to fix this is properly configuring the redirects/rewrites in amplify backend.
Amplify redirects

Check the SPA section

Add the required file extensions into the redirect regex pattern

Thank you, vrwalking. This solved my problem.

I updated the 200 rule in the Rewrites and Redirects section of AWS Amplify to include gltf and bin.

</^[^.]+$|\.(?!(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|map|json|gltf|bin)$)([^.]+$)/>