egametang/ET · error · Exception

Tile index too high

Error message

Tile index too high

What it means

When AddTile is called with a non-zero lastRef (restore a previously removed tile at its old slot), the decoded tile index is bounds-checked against m_maxTiles. An index at or beyond capacity means the ref did not come from this navmesh (different maxTiles, or corrupt/stale), so the restore is rejected before indexing m_tiles.

Source

Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Detour/DtNavMesh.cs:489

            if (lastRef == 0)
            {
                // Make sure we could allocate a tile.
                if (0 == availableTiles.Count)
                {
                    throw new Exception("Could not allocate a tile");
                }

                tile = availableTiles.First?.Value;
                availableTiles.RemoveFirst();
                m_tileCount++;
            }
            else
            {
                // Try to relocate the tile to specific index with same salt.
                int tileIndex = DecodePolyIdTile(lastRef);
                if (tileIndex >= m_maxTiles)
                {
                    throw new Exception("Tile index too high");
                }

                // Try to find the specific tile id from the free list.
                DtMeshTile target = m_tiles[tileIndex];
                // Remove from freelist
                if (!availableTiles.Remove(target))
                {
                    // Could not find the correct location.
                    throw new Exception("Could not find tile");
                }

                tile = target;
                // Restore salt.
                tile.salt = DecodePolyIdSalt(lastRef);
            }

            tile.data = data;
            tile.flags = flags;

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Only pass a lastRef produced by the same DtNavMesh in its current lifetime; pass 0 for a fresh allocation when unsure.
  2. Do not persist refs across mesh reconfigurations; re-derive them on load.
  3. If streaming with restore, keep maxTiles stable across rebuilds or invalidate cached refs when it changes.

Example fix

// before: ref from another mesh / session
mesh.AddTile(data, flags, persistedRef);

// after: fresh allocation when the ref's provenance is uncertain
mesh.AddTile(data, flags, 0);
Defensive patterns

Strategy: validation

Validate before calling

// Only restore with a ref from this mesh; otherwise allocate fresh
int idx = DecodePolyIdTile(lastRef); // helper if accessible
if (lastRef != 0 && idx >= meshMaxTiles)
    throw new ArgumentOutOfRangeException(nameof(lastRef), "lastRef index exceeds mesh maxTiles");
mesh.AddTile(data, flags, provenanceUncertain ? 0 : lastRef);

Try / catch

try { return mesh.AddTile(data, flags, lastRef); }
catch (Exception e) when (e.Message == "Tile index too high")
{ /* ref is foreign/stale; fall back to a fresh allocation */ return mesh.AddTile(data, flags, 0); }

Prevention

When it happens

Trigger: Calling AddTile(data, flags, lastRef) with a lastRef obtained from a different DtNavMesh, a ref held across a mesh rebuild with smaller maxTiles, or a corrupted/truncated ref.

Common situations: Persisting tile refs and reloading into a mesh with different params; sharing refs between meshes; refs from a prior session whose mesh config changed.

Related errors


AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13). Data as JSON: /api/errors/690d58df915c487f. Report an issue: GitHub.