FyroxEngine/Fyrox · error

Illegal nine slice position

Error message

Illegal nine slice position: {position:?}

What it means

TileDefinition nine-slice handling: nine_position_to_index converts an (x,y) position in the 0..=2 range into an index in 0..9, and panics with "Illegal nine slice position" if either coordinate exceeds 2. Nine slices are 3x3 grids; any component >= 3 has no corresponding index.

Solutions

  1. Clamp the position components before calling: position.x.min(2) and position.y.min(2), or validate 0..=2 yourself.
  2. Fix the source of the coordinate so it is derived via index_to_nine_position or bounded loops (for y in 0..3, for x in 0..3).
  3. Add an assertion/validation on your computed position and fall back to the center cell (1,1) when out of range.
  4. Use index_to_nine_position to round-trip and confirm your mapping is symmetric with this function.

Example fix

// before
let idx = TileDefinition::nine_position_to_index(Vector2::new(dx + 1, dy + 1)); // dx may be ±2
// after
let px = (dx + 1).clamp(0, 2);
let py = (dy + 1).clamp(0, 2);
let idx = TileDefinition::nine_position_to_index(Vector2::new(px, py));
Defensive patterns

Strategy: validation

Validate before calling

fn valid_nine_position(p: Vector2<usize>) -> bool {
    p.x <= 2 && p.y <= 2
}
// call site
let p = Vector2::new(px, py);
if valid_nine_position(p) {
    let idx = TileDefinition::nine_position_to_index(p);
}

Type guard

fn is_nine_position(p: Vector2<usize>) -> bool {
    p.x < 3 && p.y < 3
}

Try / catch

// Panics are unrecoverable here; clamp before the call instead
let idx = TileDefinition::nine_position_to_index(Vector2::new(
    px.min(2),
    py.min(2),
));

Prevention

When it happens

Trigger: Calling nine_position_to_index(Vector2::new(x, y)) with x or y greater than 2; computing a nine-slice position from tile neighbours without clamping to the 3x3 grid.

Common situations: Custom tile-map brushes computing offsets from neighbor deltas without wrapping/clamping; autotiling code that maps edge configurations to nine-slice coordinates incorrectly; off-by-one loops over 0..=3 instead of 0..=2.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of FyroxEngine/Fyrox@76c91aad8e (2026-09-10). Data as JSON: /api/errors/06b83a471cd13874. Report an issue: GitHub.

Appendix: source

Thrown at fyrox-impl/src/scene/tilemap/property.rs:554

            TileSetPropertyValue::NineSlice(_) => {
                TileSetPropertyValue::NineSlice(Default::default())
            }
        }
    }
    /// The type of the data in this value.
    pub fn prop_type(&self) -> TileSetPropertyType {
        match self {
            TileSetPropertyValue::I32(_) => TileSetPropertyType::I32,
            TileSetPropertyValue::F32(_) => TileSetPropertyType::F32,
            TileSetPropertyValue::String(_) => TileSetPropertyType::String,
            TileSetPropertyValue::NineSlice(_) => TileSetPropertyType::NineSlice,
        }
    }
    /// Converts an x,y position into index in 0..9. Both x and y must be within 0..3.
    #[inline]
    pub fn nine_position_to_index(position: Vector2<usize>) -> usize {
        if position.y > 2 || position.x > 2 {
            panic!("Illegal nine slice position: {position:?}");
        }
        position.y * 3 + position.x
    }
    /// Converts an index in 0..9 into an x,y position within a tile's nine slice value.
    #[inline]
    pub fn index_to_nine_position(index: usize) -> Vector2<usize> {
        let (y, x) = index.div_rem_euclid(&3);
        Vector2::new(x, y)
    }
    /// Update this value to match the given value, wherever that value is not None.
    /// Wherever the given value is None, no change is made to this value.
    pub fn set_from(&mut self, value: &TileSetPropertyOptionValue) {
        use TileSetPropertyOptionValue as OptValue;
        use TileSetPropertyValue as PropValue;
        match (self, value) {
            (PropValue::I32(x0), OptValue::I32(Some(x1))) => *x0 = *x1,
            (PropValue::F32(x0), OptValue::F32(Some(x1))) => *x0 = *x1,
            (PropValue::String(x0), OptValue::String(Some(x1))) => *x0 = x1.clone(),

View on GitHub (pinned to 76c91aad8e)