How to fix RViz2 [ERROR]: Could not load resource [....]: Unable to open file: "..."

Problem

When running ROS2’s RViz2 with a custom URDF model from file, you see an error message like this:

[ERROR] [1736351067.730994512] [rviz2]: Could not load resource [./meshes/robot_arms/fr3/visual/link0.dae]: Unable to open file "./meshes/robot_arms/fr3/visual/link0.dae".

Solution

The issue here is that a filename attribute is not treated as a filename:

<mesh filename="./meshes/robot_arms/fr3/visual/link0.dae"/>

Instead, it’s loaded using assimp_loader.getScene() using the OpenAssetImporter library like this:

const aiScene * scene = assimp_loader.getScene(resource_path);

which calls Assimp::Importer::ReadFile() here

which contains the following code:

// First check if the file is accessible at all
if( !pimpl->mIOHandler->Exists( pFile)) {

    pimpl->mErrorString = "Unable to open file \"" + pFile + "\".";
    ASSIMP_LOG_ERROR(pimpl->mErrorString);
    return nullptr;
}

There are many different IO handlers for Assimp, but rviz2 uses ResourceIOSystem which is a ROS/rviz2-specific implementation:

importer_->SetIOHandler(new ResourceIOSystem());

This is just a thin wrapper around a resource_retriever::Retriever which is located in the ros/resource_retriever project.

The implementation of the resource_retriever::Retriever is here

We now need to look at Retriever::get to see how input strings are handled.

Generally this function will pass any input string to CURL, which will then try to open the URL or file. However, Retriever::get will handle package://-schemes URLs differently.

These are essentially ROS-specific URLs and these will, in the end, be converted to absolute file://-schemed URLs, referenced to the package:// directory.

The package directory is determined here

package_path = ament_index_cpp::get_package_share_directory(package);

This function is from the ament resource index project.

Notably, this will not refer to the package root directly, but to the share directory of the package

return get_package_prefix(package_name) + "/share/" + package_name;

How to insert files directly

This tracedown shows us that rviz2 will only support URLs and not direct file paths.

To insert files directly, you can use the file://-scheme which only work with absolute URL paths since libcurl doesn’t support relativ file:// URLs

Loading files from the network

Since every non-package:// URL is passed directly to libcurl, you can also load files from the network such as using https:// or similar URLs.