louis-e/arnis · error

Failed to create tile XZBBox

Error message

Failed to create tile XZBBox

What it means

`generate_world_with_options` builds a per-tile bounding box by clamping the tile bounds (expanded by a halo) to the overall world XZ bbox and then calls `XZBBox::rect_from_min_max(...).expect("Failed to create tile XZBBox")`. `rect_from_min_max` requires min_x <= max_x and min_z <= max_z; the expect fires when the clamped rectangle is empty or inverted.

Source

Thrown at src/data_processing.rs:903

        let mut last_emitted_pct = 20.0_f64;
        // Placement-side ticks so the bar moves before the first batch merges.
        let tiles_placed = std::sync::atomic::AtomicUsize::new(0);
        for batch in indexed_tiles.chunks(tile_batch_size) {
            // Phase 1: process this batch of tiles in parallel
            let place_start = std::time::Instant::now();
            let batch_results: Vec<_> = batch
                .par_iter()
                .map(|&(tile_idx, tile_bounds)| {
                    // max_* are exclusive; rect_from_min_max treats max as inclusive,
                    // so subtract 1. Clamp to the world bbox so edge-tile halos don't
                    // extend past world bounds.
                    let tile_xzbbox = XZBBox::rect_from_min_max(
                        (tile_bounds.min_x - tile::TILE_EDITOR_HALO).max(xzbbox.min_x()),
                        (tile_bounds.min_z - tile::TILE_EDITOR_HALO).max(xzbbox.min_z()),
                        (tile_bounds.max_x - 1 + tile::TILE_EDITOR_HALO).min(xzbbox.max_x()),
                        (tile_bounds.max_z - 1 + tile::TILE_EDITOR_HALO).min(xzbbox.max_z()),
                    )
                    .expect("Failed to create tile XZBBox");

                    let mut tile_editor = WorldEditor::new(PathBuf::new(), &tile_xzbbox, llbbox);
                    tile_editor.set_ground(Arc::clone(&ground));
                    tile_editor.set_ground_origin(xzbbox.min_x(), xzbbox.min_z());
                    // Ground generation runs on tile editors, so they need the real scale.
                    tile_editor.set_projection_info(&args.projection.to_string(), args.scale);
                    tile_editor.set_place_schematics(args.use_3d);
                    tile_editor.set_map_decals(place_branding);
                    if let Some(ref tp) = tree_pack {
                        tile_editor.set_tree_pack(Arc::clone(tp));
                    }
                    tile_editor.set_sealed_surface(Arc::clone(&sealed_surface));
                    if let Some(ctx) = &signage_ctx {
                        tile_editor.set_signage(Arc::clone(ctx));
                    }
                    tile_editor.set_strict_bounds(
                        tile_bounds.min_x,
                        tile_bounds.min_z,

View on GitHub (pinned to 34048924d9)

Solutions

  1. Log tile_bounds, the halo and the world xzbbox for the failing tile and confirm the tile actually intersects the world bbox
  2. Validate the world bbox is non-degenerate (min < max on both axes) before generating tiles
  3. Use `XZBBox::rect_from_min_max` directly and skip tiles whose clamped rect is empty instead of panicking
  4. Check the projection/scale arguments; wrong scale can make tile bounds not overlap the world bbox

Example fix

// before
let tile_xzbbox = XZBBox::rect_from_min_max(x0, z0, x1, z1)
    .expect("Failed to create tile XZBBox");
// after
let Some(tile_xzbbox) = XZBBox::rect_from_min_max(x0, z0, x1, z1) else {
    eprintln!("skipping tile with no overlap: {:?}", tile_bounds);
    continue;
};
Defensive patterns

Strategy: validation

Validate before calling

let intersects = tile_bounds.min_x - halo <= xzbbox.max_x() && tile_bounds.max_x - 1 + halo >= xzbbox.min_x() && tile_bounds.min_z - halo <= xzbbox.max_z() && tile_bounds.max_z - 1 + halo >= xzbbox.min_z();
if !intersects { continue; }

Type guard

fn valid_rect(min_x: i32, min_z: i32, max_x: i32, max_z: i32) -> bool { min_x <= max_x && min_z <= max_z }

Prevention

When it happens

Trigger: A tile whose bounds, after subtracting TILE_EDITOR_HALO on min and adding it on max, do not intersect the world `xzbbox` at all — so the `.max(min_x)`/`.min(max_x)` clamping produces min_x > max_x (or min_z > max_z). This happens when tile bounds are miscomputed, the world bbox is empty/degenerate, or tile layout arithmetic is off by more than the halo.

Common situations: A world extent smaller than one tile or a zero-area bbox passed on the command line; projection/scale options that shrink the computed bbox; a bug or version change in tile grid computation producing tiles outside the world bbox.

Related errors


AI-assisted analysis of louis-e/arnis@34048924d9 (2026-09-03). Data as JSON: /api/errors/208467cb12098844. Report an issue: GitHub.