Learn / Guides /

How to find missing images in a Ren'Py project before release

March 2026 5 min read Debugging
Ren'Py Missing assets Static analysis Pre-release QA

Missing image errors in Ren'Py games share a frustrating property: they only appear when the engine tries to display the image. If the reference lives in a branch your testers never reach, the bug ships. The player finds it.

Static scanning — checking script references against files on disk before running anything — catches these before they become runtime errors. Here's how it works and what to look for.

Why missing images happen

The most common causes are not careless mistakes. They're structural:

Renamed files An image asset is renamed or moved to a new folder. Scripts still reference the old path.
Typos in scene calls A show bg_city_night call has an underscore where the filename uses a space, or vice versa.
Missing export An artist delivered a placeholder. The final asset was never added to the project.
Case sensitivity Works on Windows, breaks on Linux/Mac. BG_City.png vs bg_city.png.

None of these are caught at write time. Ren'Py scripts don't validate image paths when you type them — only when the engine needs to render them.

Why Ren'Py errors are hard to trace at runtime

When a missing image is encountered, Ren'Py produces an error like this:

Exception: Image 'bg city night' not found. While running game code: File "game/script.rpy", line 847, in script scene bg city night with dissolve

The error is clear enough on its own. The problem is that line 847 might be inside a story branch that requires a specific sequence of choices made earlier in a 40-minute playthrough. Your testers need to know that path exists, remember to take it, and happen to reach that specific scene. All three conditions have to be true simultaneously.

Why branching makes it worse A visual novel with 10 choice points where each choice produces 2 outcomes has up to 1,024 possible paths through the script. Manual testing can realistically cover a fraction of those. Missing assets in low-probability branches are exactly the kind of bug that ships to players.

How static scanning detects missing assets

Static scanning works differently from running the game. Instead of following one path through the story, it reads every .rpy file and collects every image reference — regardless of which branch it appears in.

The references it looks for include:

  • image bg_city_night = "images/bg_city_night.png" — explicit image declarations
  • show bg city night — display calls (using Ren'Py's image naming convention)
  • scene bg city with dissolve — scene transitions
  • add "images/ui_frame.png" — direct path references

Each collected reference is then checked against the files present on disk. Any reference with no matching file on disk is a missing asset.

What this catches that runtime testing misses Because the scanner reads every branch — not just the ones a tester happens to play — it finds missing references in paths that might never be reached during development. A missing asset in a bad ending, a hidden route, or a late-game optional scene will appear in the scan report alongside everything else.

Common error patterns and what they mean

Image named with spaces vs underscores

# Script references the image with spaces (Ren'Py auto-name) show bg city night # Actual file on disk: game/images/bg_city_night.png # underscore, not space # Result: Ren'Py looks for "bg city night" and cannot find it

Ren'Py maps image names to files using a specific normalisation rule. A file named bg_city_night.png is automatically available as bg city night in display calls. But if you've moved or renamed the file to bg-city-night.png (with hyphens), the mapping breaks silently.

Path reference pointing to a deleted folder

# Script uses an explicit path reference add "images/ui/overlay_frame.png" # The ui/ subfolder was reorganised during development # File is now at images/ui_elements/overlay_frame.png # The script was not updated

Placeholder reference never replaced

# Placeholder added to script while waiting for final asset show bg_final_chapter_reveal # The asset was never delivered # The label containing this call is only reached at chapter 8 endgame # Testing never reached it before release

How to run a missing asset check with BranchPy

In BranchPy's Media Validation panel, missing assets appear with a MISSING badge in the media browser. They are listed regardless of which branch contains the reference — the entire script is scanned, not just paths your testers have played.

Each MISSING entry shows the file path that was referenced and confirms it is absent from disk. No runtime required. No need to navigate to that story branch.

The key advantage over runtime discovery You see every missing reference in one list, across the entire project, before you run the game at all. A 60-second scan replaces hours of branch coverage.

What to do when you find a missing asset

  1. Check whether the file was renamed or moved — search your asset folder for the base filename, ignoring path and extension. It often still exists under a changed path.
  2. Check for case mismatches — compare the exact casing of the filename in the script reference against the file on disk.
  3. Check whether the asset was intentionally removed — if so, update the script reference. Don't leave dead references pointing at nothing.
  4. If the file was never created — create a placeholder now so the error disappears from the scan. Replace it before release.

Summary

  • Missing images only trigger at runtime, in the specific branch that references them.
  • Branching structure means those branches may never be reached during manual testing.
  • Static scanning reads every reference in the entire script, not just the paths a tester followed.
  • Every missing asset found before release is a crash your players will never see.
Analyse your Ren'Py project automatically BranchPy's Media Validation panel scans every .rpy file, detects missing asset references, and flags protected engine-managed files — directly in VS Code.

See how Media Validation works →