bevyengine/bevy · critical

ParamSet parameter validation failed: {err}

Error message

ParamSet parameter validation failed: {err}

What it means

ParamSet exposes its members through p0()..p7(); each accessor builds the member param from stored state via get_param and panics if that returns an error. The wrapped {err} is the concrete parameter validation failure - most commonly a required Res<T>/ResMut<T> inside the ParamSet whose resource does not exist in the World at call time (or state built for a different world).

Source

Thrown at crates/bevy_ecs/src/system/system_param.rs:658

                })
            }
        }

        impl<'w, 's, $($param: SystemParam,)*> ParamSet<'w, 's, ($($param,)*)>
        {
            $(
                /// Gets exclusive access to the parameter at index
                #[doc = stringify!($index)]
                /// in this [`ParamSet`].
                /// No other parameters may be accessed while this one is active.
                pub fn $fn_name<'a>(&'a mut self) -> SystemParamItem<'a, 'a, $param> {
                    // SAFETY: systems run without conflicts with other systems.
                    // Conflicting params in ParamSet are not accessible at the same time
                    // ParamSets are guaranteed to not conflict with other SystemParams
                    unsafe {
                        $param::get_param(&mut self.param_states.$index, &self.system_meta, self.world, self.change_tick)
                    }
                    .unwrap_or_else(|err| panic!("ParamSet parameter validation failed: {err}"))
                }
            )*
        }
    }
}

all_tuples_enumerated!(impl_param_set, 1, 8, P, p);

// SAFETY: Res only reads a single World resource
unsafe impl<'a, T: Resource> ReadOnlySystemParam for Res<'a, T> {}

// SAFETY: Res ComponentId access is applied to SystemMeta. If this Res
// conflicts with any prior access, a panic will occur.
unsafe impl<'a, T: Resource> SystemParam for Res<'a, T> {
    type State = ComponentId;
    type Item<'w, 's> = Res<'w, T>;

    fn init_state(world: &mut World) -> Self::State {

View on GitHub (pinned to 396ca72708)

Solutions

  1. Read the {err} text embedded in the panic - it names the exact failing param and reason.
  2. Insert required resources at startup (app.init_resource / insert_resource) before any schedule using them runs.
  3. If presence is genuinely optional, make that member Option<Res<T>>/Option<ResMut<T>> inside the ParamSet and handle None.

Example fix

// before
fn sys(mut p: ParamSet<(ResMut<Score>, Res<Config>)>) {
    let cfg = *p.p1(); // panics if Config was never inserted
}

// after
fn sys(mut p: ParamSet<(ResMut<Score>, Option<Res<Config>>)>) {
    let cfg = p.p1().as_deref().copied().unwrap_or_default();
}
Defensive patterns

Strategy: validation

Validate before calling

if !world.contains_resource::<Config>() {
    world.init_resource::<Config>();
}
// safe to fetch a ParamSet member that is Res<Config>

Prevention

When it happens

Trigger: Calling param_set.pN() where the member is Res/ResMut of a resource that was never inserted or was removed; resources inserted via Commands by an earlier system that have not been flushed (apply_deferred not yet run) when the ParamSet member is fetched.

Common situations: Systems scheduled before the system that inserts the resource; resources created lazily by other plugins; tests running param code against partially built worlds.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/6f201b62faeac15d. Report an issue: GitHub.