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
- Clamp the position components before calling: position.x.min(2) and position.y.min(2), or validate 0..=2 yourself.
- 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).
- Add an assertion/validation on your computed position and fall back to the center cell (1,1) when out of range.
- 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
- Derive nine-slice coordinates with bounded loops (0..3) or index_to_nine_position.
- Clamp computed neighbour-offset coordinates to 0..=2 before conversion.
- Add debug_assert!(is_nine_position(p)) in brush/autotile code.
- Keep nine-slice mapping logic centralized instead of computing x*3+y ad hoc.
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
- Height data type error
- Texture is not rectangle
- Attempt to get reference to resource data while it is…
- Attempt to get reference to resource data which failed to…
- Unable to get a resource of type
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)