Resource not found in exported build despite valid res:// path
0 reputation · 30 Nov 2024, 18:09 UTC
0 reputation · 30 Nov 2024, 18:09 UTC
A Godot project functions correctly within the editor on Windows and macOS but fails with a 'Resource not found' error when exported to Linux or Android. This discrepancy occurs when resource paths defined via the res:// prefix use casing that does not strictly match the actual filename on disk.
While the local development environment often utilizes a case-insensitive filesystem, the production build on Unix-based platforms enforces strict case matching. This is particularly problematic when resources are loaded dynamically via @export variables or load() calls, as the editor may resolve the path regardless of casing mismatches, while the exported virtual file system fails to locate the asset within the compressed .pck file.
How can I programmatically validate path case-sensitivity during the project export process to prevent these runtime crashes? Are there specific export filters or editor tools that can flag casing mismatches before the build is finalized?
Godot ships no built-in export filter that flags case mismatches, so you have to add the check yourself. The two practical options are: (1) a startup assertion using ResourceLoader.exists() for every path you load dynamically, run once in an exported debug build, and (2) a static scan that compares every res:// string in your .tscn, .tres, .gd, and project.godot files against the real on-disk filenames. Together these catch both serialized references and runtime load() calls before they crash on Linux or Android.
Confirmed behavior: Windows and default macOS filesystems are case-insensitive, so load("res://Scenes/Main.tscn") succeeds in the editor even when the file is actually main.tscn. Exported PCK archives and the Linux/Android filesystems are case-sensitive, so the same lookup returns null at runtime with the "Resource not found" failure. The export log does not report this — the mismatch only surfaces when the resource is requested.
Scenes and resources store paths as literal strings, so a script can extract every res://... reference and verify each segment's casing against the directory listing. A minimal check from the command line (run on Linux, or in CI):
grep -rhoE 'res://[^"')]+' --include='*.gd' --include='*.tscn' --include='*.tres' --include='project.godot' . | sort -u | while read -r p; do
f="${p#res://}"
[ -e "$f" ] || echo "MISSING or case-mismatched: $p"
doneOn a case-sensitive filesystem, [ -e ] fails for any casing mismatch. You can also do this inside the editor with an EditorScript using DirAccess, comparing each referenced path segment-by-segment against actual directory entries.
Static scanning cannot see paths assembled by string concatenation. For those, assert at startup:
for path in dynamic_paths:
if not ResourceLoader.exists(path):
push_error("Unresolved resource path: %s" % path)Export a debug build, run it once on the target platform (or any Linux machine), and every unresolved path is logged.
If you want this enforced during export, a custom EditorExportPlugin can inspect each file in its export callbacks and warn when a referenced path's casing doesn't match the file being packed. Note that the callback names and signatures differ between Godot 3.x and 4.x — verify against your version before relying on example code.
Running the project from source on Linux (or in a CI container) reproduces the failure without exporting at all, since the editor there uses the case-sensitive filesystem directly. Many teams simply add this as a CI step. Longer term, adopting an all-lowercase snake_case naming convention — and renaming via the FileSystem dock so dependencies update — removes the entire class of errors. When committing pure case renames, remember Git on case-insensitive systems may not record them; use git mv with an intermediate name.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.