Bevy 0.17 Asset Hot Reload Silent - PNG Watcher Not Triggering After 0.16 Upgrade - How to Fix
Problem: You upgraded from Bevy 0.16 to 0.17, enabled asset hot reload in AssetPlugin, and run with cargo run. Saving assets/player.png in Aseprite, Krita, or Photoshop does not update the sprite on screen. No panic, no log spam — the watcher is simply silent.
Who is affected now: Windows developers using art tools that save via temp-file-then-rename (common in Aseprite), teams that assumed hot reload was on because DefaultPlugins changed defaults in 0.17, and repos that forgot the file_watcher Cargo feature after copying a minimal Cargo.toml from a WASM template.
Fastest safe fix: Set watch_for_changes_override: Some(true) explicitly, add features = ["file_watcher"] on the bevy dependency, confirm the PNG path is under assets/ (not embedded), and verify with touch assets/player.png — if touch works but Aseprite save does not, apply the atomic-rename workaround below.
Direct answer
Bevy 0.17 can hot-reload assets, but only when three gates pass: the file_watcher feature is compiled in, AssetPlugin has watch_for_changes_override: Some(true), and the OS file watcher sees the save event. Many 0.16 → 0.17 upgrades satisfy none of the three because templates differ and art tools on Windows do not emit modify events for atomic renames. Turning the override on and enabling the feature fixes the majority of reports; the remainder need a save-path or polling fallback.
Why this issue spikes in 2026
- Bevy 0.16/0.17 marketed default-friendly hot reload — teams enabled it mentally but not in
Cargo.toml. - Mid-2026 Discord volume clusters on Windows + Aseprite atomic saves after indie teams adopted Bevy for 2D platformer jams.
- Guide and blog content shows hot reload in snippets without repeating the
file_watcherfeature line every time.
Pair this fix with the Bevy 0.17 Required Components, Observers, and Asset Hot Reload guide chapter for the full modernization path.
Symptoms and phrases to match
- Texture unchanged after Ctrl+S in Aseprite;
cargo runrestart shows new art. watch_for_changes_overridecommented out or missing after merge from0.16template.- Works on macOS for one teammate, silent on Windows for another (atomic rename).
Imageloaded viainclude_bytes!orAsset::embedded— watcher never applies.- Saving to
art/source/player.pngwhile the game loadsassets/player.png.
Root causes (check in this order)
file_watcherfeature not enabled inCargo.toml.watch_for_changes_overrideleftNone— 0.17 still requires explicitSome(true)in many project templates.- Wrong folder — file saved outside
assets/root theAssetServerwatches. - Embedded asset — no filesystem path to watch.
- Atomic rename save on Windows (Aseprite default) —
notifymay not fireModifyon the final path. - WASM build target — hot reload is intentionally absent; verify you are not testing
wasm32while reading desktop docs.
Fastest safe fix path
Step 1 - Enable the Cargo feature
In Cargo.toml:
[dependencies]
bevy = { version = "0.17", features = ["file_watcher"] }
Run cargo clean once after adding the feature so the watcher backend links in.
Step 2 - Set AssetPlugin override explicitly
use bevy::asset::AssetPlugin;
use bevy::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins.set(AssetPlugin {
watch_for_changes_override: Some(true),
..default()
}))
.run();
}
Do not rely on “defaults changed in 0.17” without this line in your app entry.
Step 3 - Confirm the asset path
Your load call should reference a project path:
// Good — watched file
commands.spawn(Sprite::from_image(asset_server.load("player.png")));
// Not watched — embedded
// let handle: Handle<Image> = asset_server.load_embedded_asset!(...);
The file on disk must be assets/player.png (or a subfolder under assets/).
Step 4 - Baseline test with touch
With the game running:
# macOS / Linux
touch assets/player.png
# Windows PowerShell
(Get-Item assets/player.png).LastWriteTime = Get-Date
If the sprite updates within ~250 ms, the watcher works — your art tool save path is the remaining issue.
Step 5 - Aseprite atomic rename workaround (Windows)
If touch works but Aseprite does not:
- In Aseprite, use File > Save As occasionally to force an in-place write, or
- Enable File > Preferences > Files > Export files with "Save As" format only for export — for working files, save to the
assets/path directly, or - Add a dev-only R key that calls
asset_server.reload(&handle)for the active hero texture during art sprints.
Alternative fix paths
A) Dev-only hot reload plugin (desktop only)
#[cfg(not(target_arch = "wasm32"))]
fn hot_reload_plugin(app: &mut App) {
app.add_plugins(DefaultPlugins.set(AssetPlugin {
watch_for_changes_override: Some(true),
..default()
}));
}
Gate WASM builds separately — they should not enable file_watcher.
B) Polling fallback for jam builds
For game-jam scope only, a system that reloads a handle every N seconds in cfg(debug_assertions) is acceptable. Remove before ship — it masks production asset bugs.
C) Copy-through save script
Save Aseprite sources in art/, then run a watch script that cp into assets/ (copy triggers modify events reliably on Windows).
Verification checklist
- [ ]
cargo tree -i bevyshowsfile_watcherfeature enabled. - [ ]
watch_for_changes_override: Some(true)present inmain.rs(or shared plugin). - [ ]
touch assets/player.pngupdates the sprite in under 1 second. - [ ] Aseprite Save updates the sprite after workaround, or documented reload key works.
- [ ] No
load_embedded_asset!on the texture under test.
Prevention
- Pin Bevy to a minor version in
Cargo.toml(0.17.*) and document hot-reload requirements inREADME.md. - Add a CI smoke job that runs
touchon a golden PNG in a headlessApptest (where supported). - Keep art sources and runtime assets paths distinct; only
assets/is watched. - Ship the 15 Free Bevy 0.17 Indie Iteration Resources link bundle in onboarding docs.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
| Nothing ever reloads | Missing file_watcher feature |
Step 1 |
| Reload on Linux only | Windows atomic rename | Step 5 |
| Reload once then stops | Handle replaced / entity despawned | Reload same Handle, same entity |
| Panic on reload | Image size changed | Keep export dimensions fixed per handle |
| Works in Editor tool, not game | Wrong assets/ root |
Align path with AssetPlugin folder |
Frequently asked questions
Q: Does hot reload recompile Rust?
A: No. Only assets on disk (PNG, RON, WGSL, etc.). Code changes still need cargo run.
Q: Should I enable this in release builds?
A: Usually no. Gate with #[cfg(debug_assertions)] or a dev feature flag.
Q: Bevy 0.16 worked without file_watcher — why?
A: Older templates and docs differed; 0.17 documents hot reload as ergonomic but still feature-gated in Cargo.
Related help articles and guides
- Bevy 0.17 Required Components, Observers, and Asset Hot Reload (2026) — full modernization chapter including hot reload setup.
- Bevy 0.17 "Component Is Already Required" Panic — fix startup panic before debugging silent reload.
- Your First Bevy 0.17 2D Platformer in One Weekend (2026) — hands-on loop that assumes working texture reload.
- 15 Free Bevy 0.17 Indie Iteration Resources (2026 Q3) — official docs, examples, and profiler links.
- Clip Studio Paint Timeline to Sprite Sheet Unity and Godot Handoff (2026) — if art ships from CSP into
assets/for Bevy consumption.
Bookmark this article before your next 0.16 → 0.17 upgrade sprint so art iteration does not stall on silent watchers.