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

  1. Only call RemoveTile with a refs returned by AddTile on the same DtNavMesh instance.
  2. Rebuild or re-query the tile ref after any navmesh reconstruction and discard old handles.
  3. Guard the call by decoding the index and comparing against m_maxTiles before invoking RemoveTile.
  4. 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

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


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