Bevy 0.17 "Component Is Already Required" Panic - Sprite Migration From SpriteBundle - How to Fix
Problem: After upgrading to Bevy 0.17, App::run() panics immediately at startup:
Component `Transform` is already required by component `Sprite`
You replaced SpriteBundle { ... } with Sprite::from_image(...) per the new docs, but the app never reaches your first system.
Who is affected now: Teams porting 0.13/0.14 tutorial code in 2026, plugins that add their own #[require(Transform)] on leaf components, and projects that register plugins in an order that registers the same required edge twice.
Fastest safe fix: Read the panic for both component names (X already required by Y), remove the duplicate #[require(...)] on your custom component (or stop manually requiring what Bevy core already requires), then reorder add_plugins so framework plugins register before game plugins.
Direct answer
Bevy 0.17 required components attach defaults automatically when you spawn Sprite, Camera2d, or Node. The panic means two different components in the type graph both declared the same required edge — usually your #[require(Transform)] on a custom component plus Bevy’s built-in requirements on Sprite. Delete the redundant require, fix plugin registration order, and spawn from a single root without chaining duplicate requires on parent and child.
Why this issue spikes in 2026
- Required components replaced bundles as the default teaching path in 0.16/0.17 (Q1–Q2 2026).
- Most indie Bevy repos still mix 0.13-era chapter code with new spawn snippets.
- Third-party plugins copied
#[require(Transform)]patterns from pre-0.17 blog posts.
Pair the fix with the Bevy 0.17 Required Components, Observers, and Asset Hot Reload guide chapter and, if textures do not refresh after you fix the panic, Bevy 0.17 Asset Hot Reload Silent.
Symptoms and phrases to match
- Panic before window opens;
cargo testalso fails onAppstartup. - Message names two components — not a missing component error.
- Occurs right after deleting
SpriteBundle/Camera2dBundle. - Adding
#[require(Transform)]on bothPlayerand a childWeaponsprite. - Panic only when a specific third-party plugin is enabled.
Root causes (check in this order)
- Duplicate
#[require]— your component requiresTransformwhile spawning underSprite(which already requiresTransform). - Plugin double-registration — two plugins register conflicting required-component metadata for the same pair.
- Non-deterministic plugin order —
add_pluginsorder changes which require wins until panic. - Tuple spawn with redundant requires — parent and child both carry the same require path.
- Copied 0.13 bundle + 0.17 sprite in the same spawn function.
Fastest safe fix path
Step 1 - Parse the panic (both sides matter)
Example:
Component `Transform` is already required by component `Sprite`
- Already required:
Transform(the dependency being attached twice). - Required by:
Sprite(Bevy core already owns this edge).
If your custom Player also has #[require(Transform)], remove it from Player when you spawn with Sprite:
// Before (conflicts with Sprite's built-in requires)
#[derive(Component)]
#[require(Transform, Visibility)]
struct Player;
// After (let Sprite supply Transform / Visibility)
#[derive(Component)]
struct Player;
Step 2 - One root spawn pattern
Prefer a single spawn graph:
commands.spawn((
Sprite::from_image(asset_server.load("player.png")),
Transform::from_xyz(0.0, 0.0, 0.0),
Player,
));
Do not also #[require(Transform)] on Player unless Player spawns without Sprite.
Step 3 - Reorder plugins
Register Bevy defaults before game plugins that touch required components:
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(MyGamePlugin) // after DefaultPlugins
.run();
If the panic disappears when you disable MyGamePlugin, inspect that plugin’s #[require(...)] derives.
Step 4 - Startup spawn test (CI guard)
#[test]
fn app_starts_without_required_component_panic() {
App::new()
.add_plugins(MinimalPlugins)
.add_systems(Startup, setup_player_sprite)
.update();
}
Run on every PR that touches #[require] or plugin registration.
Alternative branches
Branch A — Custom component must always have Transform
Spawn Player without Sprite when you need a bare transform:
commands.spawn((
Player,
Transform::default(),
Visibility::default(),
));
Branch B — Third-party plugin conflict
Temporarily binary-search plugins. File an issue with the plugin author: required edges must not duplicate Bevy core’s graph.
Branch C — Still on 0.16 tutorials
Pin bevy = "0.17" in Cargo.toml and complete the modernization chapter in one pass — mixing bundle and require APIs in one file always panics.
Verification checklist
- [ ]
cargo runreaches first frame without panic. - [ ]
bevy_dev_tools/ ECS inspector showsTransformonce on player entity. - [ ]
cargo teststartup test passes in CI. - [ ] No
#[require(Transform)]on components spawned withSprite. - [ ] Plugin order documented in
lib.rs/main.rscomment.
Prevention
- Pin minor Bevy version in
Cargo.toml; bump only with a migration checklist. - Ban new
#[require(Transform)]on sprite-tagged components in code review. - Add the startup spawn test above to the default template.
- When importing 0.13 snippets, run
rg "SpriteBundle|Camera2dBundle"and replace in one commit.
Troubleshooting table
| Symptom | Likely cause | Fix |
|---|---|---|
Panic names Sprite + Transform |
Duplicate require on custom component | Remove #[require(Transform)] from leaf |
| Panic only with UI + sprites | Node and custom UI require clash |
Separate plugins; check Node requires |
| Intermittent panic on clean build | Plugin order differs debug vs release | Fix add_plugins order explicitly |
| Panic after adding derive macro | Proc-macro injected #[require] |
Inspect expanded code / disable macro |
| Compile OK, panic at runtime | Required graph validated at startup | Use startup test, not only cargo check |
Frequently asked questions
Q: Should I add #[require(Transform)] on every game component?
A: Only when that component spawns without Bevy types that already require Transform. Sprites, cameras, and UI nodes already carry the graph.
Q: Can I keep SpriteBundle on 0.17?
A: Bundles are removed/deprecated on the modernization path — migrate to Sprite::from_image + explicit overrides.
Q: Does this affect observers or hot reload?
A: No — but fix the panic first; otherwise you never reach systems that use observers or reload assets.
Q: Panic mentions a third-party component name?
A: That plugin registered a conflicting require — reorder plugins or patch the plugin version.
Related help articles and guides
- Bevy 0.17 Required Components, Observers, and Asset Hot Reload (2026) — authoritative migration chapter.
- Bevy 0.17 Asset Hot Reload Silent — next failure mode after spawn works.
- Your First Bevy 0.17 2D Platformer in One Weekend (2026) — end-to-end loop.
- 15 Free Bevy 0.17 Indie Iteration Resources (2026 Q3) — docs and examples index.
Bookmark this page when your 0.16 → 0.17 port hits a startup panic — the fix is almost always a duplicate require edge, not a broken sprite image.