egametang/ET · error · Exception
Invalid tile index
Error message
Invalid tile index
What it means
Thrown by DtNavMesh.RemoveTile after decoding the tile index from the supplied refs. If the decoded index is >= m_maxTiles, the reference cannot correspond to any slot in the m_tiles array, so it is rejected as out of range. This almost always means refs is corrupted, truncated, or belongs to a different navmesh configuration.
Source
Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Detour/DtNavMesh.cs:581
/// @param[out] data Data associated with deleted tile.
/// @param[out] dataSize Size of the data associated with deleted tile.
///
/// This function returns the data for the tile so that, if desired,
/// it can be added back to the navigation mesh at a later point.
///
/// @see #addTile
public long RemoveTile(long refs)
{
if (refs == 0)
{
return 0;
}
int tileIndex = DecodePolyIdTile(refs);
int tileSalt = DecodePolyIdSalt(refs);
if (tileIndex >= m_maxTiles)
{
throw new Exception("Invalid tile index");
}
DtMeshTile tile = m_tiles[tileIndex];
if (tile.salt != tileSalt)
{
throw new Exception("Invalid tile salt");
}
// Remove tile from hash lookup.
GetTileListByPos(tile.data.header.x, tile.data.header.y).Remove(tile);
// Remove connections to neighbour tiles.
// Create connections with neighbour tiles.
// Disconnect from other layers in current tile.
List<DtMeshTile> nneis = GetTilesAt(tile.data.header.x, tile.data.header.y);
foreach (DtMeshTile j in nneis)
{View on GitHub (pinned to 5cab01f7a8)
Solutions
- Only call RemoveTile with a refs returned by AddTile on the same DtNavMesh instance.
- Rebuild or re-query the tile ref after any navmesh reconstruction and discard old handles.
- Guard the call by decoding the index and comparing against m_maxTiles before invoking RemoveTile.
- Audit serialization paths for truncation of the 64-bit refs value.
Example fix
// before
mesh.RemoveTile(refsFromAnotherMesh);
// after
int idx = DtNavMesh.DecodePolyIdTile(refs);
if (idx < mesh.GetMaxTiles())
mesh.RemoveTile(refs);
else
Debug.LogWarning("Ignoring stale/cross-mesh tile ref"); Defensive patterns
Strategy: validation
Validate before calling
int idx = DtNavMesh.DecodePolyIdTile(refs);
if (idx >= mesh.GetMaxTiles() || refs == 0)
return; // ignore out-of-range/stale ref
mesh.RemoveTile(refs); Type guard
static bool IsPlausibleTileRef(DtNavMesh mesh, long refs)
=> refs != 0 && DtNavMesh.DecodePolyIdTile(refs) < mesh.GetMaxTiles(); Try / catch
try { mesh.RemoveTile(refs); }
catch (Exception e) when (e.Message.Contains("Invalid tile index"))
{ Debug.LogWarning($"Ignored out-of-range tile ref {refs}"); } Prevention
- Always obtain refs from AddTile on the same mesh instance.
- Discard persisted refs after rebuilding the navmesh.
- Decode and range-check the index before calling RemoveTile.
- Serialize refs as full 64-bit values; guard against truncation.
When it happens
Trigger: Calling RemoveTile(refs) with a refs value from another DtNavMesh built with a different m_maxTiles; passing a half-overwritten long; using a poly ref where a tile ref is expected; reusing a refs after rebuilding the navmesh with a different tile capacity.
Common situations: Persisting refs across sessions where the navmesh was rebuilt with different tile counts; mixing up polygon refs and tile refs; corrupting the handle during serialization.
Related errors
- Could not find tile
- Invalid tile salt
- Invalid magic
- Invalid number of verts per poly {header.maxVertsPerPoly}
- Invalid magic {header.magic}
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/98b9e896baaf7097.
Report an issue: GitHub.