egametang/ET · error · Exception

Could not find tile

Error message

Could not find tile

What it means

Thrown by DtNavMesh.AddTile when restoring a previously-removed tile via a non-zero lastRef. The code decodes the tile index from lastRef, looks up m_tiles[tileIndex], then tries to remove that tile object from the availableTiles free list. If the tile is not on the free list (already reallocated, never freed, or lastRef points at a live tile), AddTile aborts. It indicates the lastRef handle is stale or points to a slot that is not actually free.

Source

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

                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;
            tile.links.Clear();
            tile.polyLinks = new int[data.polys.Length];
            Array.Fill(tile.polyLinks, DtNavMesh.DT_NULL_LINK);

            // Insert tile into the position lut.
            GetTileListByPos(header.x, header.y).Add(tile);

            // Patch header pointers.

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Pass lastRef=0 for a fresh add so the navmesh allocates a new slot from the free list instead of relocating.
  2. Discard any cached lastRef after a navmesh rebuild or after the corresponding tile has been re-added by another caller.
  3. Track the live/freed state of refs yourself and only pass a lastRef that was returned by a RemoveTile you performed on the same DtNavMesh instance.
  4. Verify m_tileCount / free-list capacity before re-adding when streaming tiles concurrently.

Example fix

// before
long oldRef = mesh.AddTile(data, 0, storedLastRef); // storedLastRef may be stale

// after
long oldRef = storedLastRefIsValid
    ? mesh.AddTile(data, 0, storedLastRef)
    : mesh.AddTile(data, 0, 0); // fresh allocation
Defensive patterns

Strategy: try-catch

Validate before calling

// Guard a restore-add: only reuse lastRef if the slot is still free-ish.
int idx = DtNavMesh.DecodePolyIdTile(lastRef);
bool safe = idx < mesh.GetMaxTiles() && lastRef != 0 && refStillTracked;
long ref2 = mesh.AddTile(data, flags, safe ? lastRef : 0);

Type guard

static bool IsValidRestoreRef(DtNavMesh mesh, long lastRef)
    => lastRef != 0 && DtNavMesh.DecodePolyIdTile(lastRef) < mesh.GetMaxTiles();

Try / catch

try { mesh.AddTile(data, flags, lastRef); }
catch (Exception e) when (e.Message.Contains("Could not find tile"))
{
    // lastRef stale -> fall back to a fresh allocation
    mesh.AddTile(data, flags, 0);
}

Prevention

When it happens

Trigger: Calling AddTile(data, flags, lastRef) with a lastRef obtained from a tile that has since been re-added, re-removed, or was never removed; passing a refs belonging to a different DtNavMesh instance; reusing a stored lastRef across navmesh rebuilds that changed m_maxTiles.

Common situations: Streaming tile pipelines that cache a tile's old ref and try to re-add it after the slot was reused; serializing/deserializing navmesh state and feeding a stale lastRef back; calling AddTile with lastRef from RemoveTile but the navmesh was reconstructed in between.

Related errors


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