Summary
This may be the wrong place to post this, but I'm not entirely sure where this issue belongs. I encountered the issue when trying to implement expo-router in one of my projects.
While I was converting over to metro from webpack in order to enable the use of expo-router, I ran into issues running the application on web. Images would always error with the message: Uncaught Error: Image: asset with ID "108" could not be found. Please check the image source or packager.
After digging in and debugging with a co-worker, it seems like this error only happens on Windows (Mac OS runs fine locally).
I also checked the Source tab in the browser and added some breakpoints to the bundle to check how assets were being loaded/used. To my surprise, it looked like the registerAsset
method in the bundle wasn't even running (causing the getAsssetById
method to throw an error since our asset array was empty).
This was the relevant block of code on Windows that is supposed to handle asset registration/retrieval:
__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getAssetByID = getAssetByID;
exports.registerAsset = registerAsset;
var assets = [];
function registerAsset(asset) {
return assets.push(asset);
}
function getAssetByID(assetId) {
return assets[assetId - 1];
}
},212,[],"..\\..\\node_modules\\react-native-web\\dist\\modules\\AssetRegistry\\index.js");
After adding breakpoints, it was clear that the registerAsset
method was not being invoked and the following block ended up throwing errors since the asset came back null:
function resolveAssetUri(source) {
var uri = null;
if (typeof source === 'number') {
var asset = (0, _AssetRegistry.getAssetByID)(source);
if (asset == null) {
throw new Error("Image: asset with ID \"" + source + "\" could not be found. Please check the image source or packager.");
}
var scale = asset.scales[0];
if (asset.scales.length > 1) {
var preferredScale = _PixelRatio.default.get();
scale = asset.scales.reduce(function (prev, curr) {
return Math.abs(curr - preferredScale) < Math.abs(prev - preferredScale) ? curr : prev;
});
}
var scaleSuffix = scale !== 1 ? "@" + scale + "x" : '';
uri = asset ? asset.httpServerLocation + "/" + asset.name + scaleSuffix + "." + asset.type : '';
} else if (typeof source === 'string') {
uri = source;
} else if (source && typeof source.uri === 'string') {
uri = source.uri;
}
if (uri) {
var match = uri.match(svgDataUriPattern);
if (match) {
var prefix = match[1],
svg = match[2];
var encodedSvg = encodeURIComponent(svg);
return "" + prefix + encodedSvg;
}
}
return uri;
}
Digging EVEN DEEPER, it looks like the registerAsset
method that was being used by the bundler was from a completely separate module react-native/Libraries/Image/AssetRegistry
=> ../../node_modules/@react-native/assets/registry.js
(I added breakpoints in that module and do see it running):
__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
module.exports = _$$_REQUIRE(_dependencyMap[0], "react-native/Libraries/Image/AssetRegistry").registerAsset({
"__packager_asset": true,
"httpServerLocation": "/assets/../../node_modules/expo-router/assets",
"width": 48,
"height": 48,
"scales": [1],
"hash": "b2de8e638d92e0f719fa92fa4085e02a",
"name": "forward",
"type": "png",
"fileHashes": ["b2de8e638d92e0f719fa92fa4085e02a"]
});
},580,[379],"..\\..\\node_modules\\expo-router\\assets\\forward.png");
So, my next question was - why is MacOS working? I had my coworker pull his bundle and noticed that the registerAsset block referenced a different module there. His referenced the same module that I saw was actually running on Windows ../../node_modules/@react-native/assets/registry.js
:
module.exports = _$$_REQUIRE(_dependencyMap[0], "@react-native/assets/registry");
},201,[202],"../../node_modules/react-native/Libraries/Image/AssetRegistry.js");
__d(function (global, _$$_REQUIRE, _$$_IMPORT_DEFAULT, _$$_IMPORT_ALL, module, exports, _dependencyMap) {
'use strict';
var assets = [];
function registerAsset(asset) {
return assets.push(asset);
}
function getAssetByID(assetId) {
return assets[assetId - 1];
}
module.exports = {
registerAsset: registerAsset,
getAssetByID: getAssetByID
};
},202,[],"../../node_modules/@react-native/assets/registry.js");
So my assumption is that the module reference at the end of that block serves to override the module's handler so that it runs correctly. On Windows, it is trying to use ..\\..\\node_modules\\react-native-web\\dist\\modules\\AssetRegistry\\index.js
, which just seems wrong. MacOS is overriding the correct module ../../node_modules/@react-native/assets/registry.js
and thus doesn't have the issue.
Minimal reproducible example
https://github.com/kylegwalsh/expo-router-bundle-error-example