bevyengine/bevy · error · CubicNurbsError

Invalid knots: contains descending knot pair

Error message

Invalid knots: contains descending knot pair

What it means

Knot vectors must be a non-decreasing sequence: any adjacent pair with knots[i] > knots[i+1] returns DescendingKnots (cubic_splines/mod.rs:667-669). Repeated values are allowed (knot multiplicity, useful for sharp corners); only decreases are invalid.

Source

Thrown at crates/bevy_math/src/cubic_splines/mod.rs:537

            Ok(CubicCurve { segments })
        }
    }
}

/// Error during construction of [`CubicNurbs`]
#[derive(Clone, Debug, Error)]
pub enum CubicNurbsError {
    /// Provided the wrong number of knots.
    #[error("Wrong number of knots: expected {expected}, provided {provided}")]
    KnotsNumberMismatch {
        /// Expected number of knots
        expected: usize,
        /// Provided number of knots
        provided: usize,
    },
    /// The provided knots had a descending knot pair. Subsequent knots must
    /// either increase or stay the same.
    #[error("Invalid knots: contains descending knot pair")]
    DescendingKnots,
    /// The provided knots were all equal. Knots must contain at least one increasing pair.
    #[error("Invalid knots: all knots are equal")]
    ConstantKnots,
    /// Provided a different number of weights and control points.
    #[error("Incorrect number of weights: expected {expected}, provided {provided}")]
    WeightsNumberMismatch {
        /// Expected number of weights
        expected: usize,
        /// Provided number of weights
        provided: usize,
    },
    /// The number of control points provided is less than 4.
    #[error("Not enough control points, at least 4 are required, {provided} were provided")]
    NotEnoughControlPoints {
        /// The number of control points provided
        provided: usize,
    },

View on GitHub (pinned to 396ca72708)

Solutions

  1. Sort the knot vector ascending before passing it (stable repeats preserved).
  2. Validate with knots.windows(2).all(|w| w[0] <= w[1]) at load time.
  3. Pass None for knots if custom parameterization is not required.

Example fix

// before
let knots = [5.0, 1.0, 2.0, 3.0, 4.0, 4.0, 5.0, 0.0]; // descending pairs -> error

// after
let mut knots = knots;
knots.sort_by(|a, b| a.partial_cmp(b).unwrap());
let nurbs = CubicNurbs::new(pts, None, Some(knots))?;
Defensive patterns

Strategy: validation

Validate before calling

fn knots_nondecreasing(knots: &[f32]) -> bool {
    knots.windows(2).all(|w| w[0] <= w[1])
}

let knots = {
    let mut k = knots.to_vec();
    k.sort_by(|a, b| a.partial_cmp(b).unwrap());
    k
};
assert!(knots_nondecreasing(&knots));

Try / catch

match CubicNurbs::new(points, None, Some(knots)) {
    Err(CubicNurbsError::DescendingKnots) => { /* sort knots ascending and retry */ }
    Ok(n) => { /* use n */ }
    Err(e) => warn!("{e}"),
}

Prevention

When it happens

Trigger: Passing an unsorted or hand-edited knot vector; importing parameterizations that run descending; sign mistakes or transposed values in long vectors.

Common situations: Manually authored knot arrays in level/tooling data; knots exported from DCC tools with reversed parameters; copy-paste errors in 8+ element vectors.

Related errors


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