stride3d/stride · error · ArgumentException
Duration provided must be greater than zero
Error message
Duration provided must be greater than zero
What it means
The FixedTimeStep property setter on BepuSimulation validates that the configured physics tick duration is strictly positive (value.Ticks > 0). A zero or negative TimeSpan would cause the simulation loop to spin or divide by zero when computing tick counts, so the setter rejects it with ArgumentException before mutating state.
Solutions
- Pass a strictly positive TimeSpan, e.g. sim.FixedTimeStep = TimeSpan.FromSeconds(1.0 / 60.0);
- Validate the configured value before assignment: if (ticks <= 0) throw/log before touching the property.
- Fix the source of the config value (settings file, YAML/JSON) so it contains a positive duration.
Example fix
// before sim.FixedTimeStep = TimeSpan.Zero; // after sim.FixedTimeStep = TimeSpan.FromSeconds(1.0 / 60.0);
Defensive patterns
Strategy: validation
Validate before calling
if (timeSpan != null && timeSpan.Ticks > 0) {
sim.FixedTimeStep = timeSpan;
} else {
throw new ArgumentException("FixedTimeStep must be positive");
} Try / catch
try { sim.FixedTimeStep = configured; } catch (ArgumentException ex) { log.Error("Invalid fixed time step", ex); sim.FixedTimeStep = TimeSpan.FromSeconds(1.0/60.0); } Prevention
- Always initialize timestep from a positive constant like TimeSpan.FromSeconds(1.0/60.0)
- Validate config-derived durations immediately after loading
- Never use default(TimeSpan) as a timestep
When it happens
Trigger: Assigning BepuSimulation.FixedTimeStep to TimeSpan.Zero, a negative TimeSpan (e.g. TimeSpan.FromSeconds(-0.016)), or an uninitialized TimeSpan field that was never set.
Common situations: Config-driven simulation setup where the fixed timestep comes from a settings file that was never filled in, or a default(TimeSpan) field passed through; also typos like TimeSpan.FromSeconds(-1/60).
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- . should be greater than .
- Thread count must be positive.
- 's must be finite and greater than zero
- is not supported in height type. Or . doesn't match .
- . is not enough to copy.
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/98ebf5f9b66dcc3a.
Report an issue: GitHub.
Appendix: source
Thrown at sources/engine/Stride.BepuPhysics/Stride.BepuPhysics/BepuSimulation.cs:258
/// Multiplier over <see cref="SolverSubStep"/> during Soft Start
/// </summary>
[Display(13, "SoftStart Substep factor", CategoryConstraints)]
public int SoftStartSubstepFactor { get; set; } = 4;
/// <summary>
/// The amount of time between individual simulation steps/ticks, by default ~16.67 milliseconds which would run 60 ticks per second
/// </summary>
/// <remarks>
/// Larger values improve performance at the cost of stability and precision.
/// </remarks>
[DataMemberIgnore]
public TimeSpan FixedTimeStep
{
get => _fixedTimeStep;
set
{
if (value.Ticks <= 0)
throw new ArgumentException("Duration provided must be greater than zero");
_fixedTimeStep = value;
}
}
#region UglyEditorWorkaroundForMasks
// This nonsense is temporary, we need a specific editor UI for this
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer0 { get => CollisionMatrix.Get(CollisionLayer.Layer0); set => CollisionMatrix.Set(CollisionLayer.Layer0, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer1 { get => CollisionMatrix.Get(CollisionLayer.Layer1); set => CollisionMatrix.Set(CollisionLayer.Layer1, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer2 { get => CollisionMatrix.Get(CollisionLayer.Layer2); set => CollisionMatrix.Set(CollisionLayer.Layer2, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer3 { get => CollisionMatrix.Get(CollisionLayer.Layer3); set => CollisionMatrix.Set(CollisionLayer.Layer3, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer4 { get => CollisionMatrix.Get(CollisionLayer.Layer4); set => CollisionMatrix.Set(CollisionLayer.Layer4, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer5 { get => CollisionMatrix.Get(CollisionLayer.Layer5); set => CollisionMatrix.Set(CollisionLayer.Layer5, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer6 { get => CollisionMatrix.Get(CollisionLayer.Layer6); set => CollisionMatrix.Set(CollisionLayer.Layer6, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer7 { get => CollisionMatrix.Get(CollisionLayer.Layer7); set => CollisionMatrix.Set(CollisionLayer.Layer7, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer8 { get => CollisionMatrix.Get(CollisionLayer.Layer8); set => CollisionMatrix.Set(CollisionLayer.Layer8, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer9 { get => CollisionMatrix.Get(CollisionLayer.Layer9); set => CollisionMatrix.Set(CollisionLayer.Layer9, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer10 { get => CollisionMatrix.Get(CollisionLayer.Layer10); set => CollisionMatrix.Set(CollisionLayer.Layer10, value); }
[DefaultValue(CollisionMask.Everything), Display(category:MaskCategory)] public CollisionMask Layer11 { get => CollisionMatrix.Get(CollisionLayer.Layer11); set => CollisionMatrix.Set(CollisionLayer.Layer11, value); }View on GitHub (pinned to 96fad776d2)