egametang/ET · error · Exception

Invalid tile ref

Error message

Invalid tile ref

What it means

DtTileCache.RemoveTile refuses a reference of 0 because zero is the library's sentinel for 'no tile' (a real tile ref always carries a non-zero salt/index encoding). Passing 0 means the caller never had, or already lost, the reference and there is nothing to remove.

Source

Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Detour.TileCache/DtTileCache.cs:290

            // Init tile.
            tile.header = header;
            tile.data = data;
            tile.compressed = Align4(buf.Position());
            tile.flags = flags;

            return GetTileRef(tile);
        }

        private int Align4(int i)
        {
            return (i + 3) & (~3);
        }

        public void RemoveTile(long refs)
        {
            if (refs == 0)
            {
                throw new Exception("Invalid tile ref");
            }

            int tileIndex = DecodeTileIdTile(refs);
            int tileSalt = DecodeTileIdSalt(refs);
            if (tileIndex >= m_params.maxTiles)
            {
                throw new Exception("Invalid tile index");
            }

            DtCompressedTile tile = m_tiles[tileIndex];
            if (tile.salt != tileSalt)
            {
                throw new Exception("Invalid tile salt");
            }

            // Remove tile from hash lookup.
            int h = DtNavMesh.ComputeTileHash(tile.header.tx, tile.header.ty, m_tileLutMask);
            DtCompressedTile prev = null;

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Guard the call: only RemoveTile when refs != 0.
  2. Reset your stored ref to 0 after a successful RemoveTile so a repeated unload is a no-op.
  3. Log when you attempt to remove a 0 ref so stale unload requests are visible.

Example fix

// before
cache.RemoveTile(storedRef);

// after
if (storedRef != 0)
{
    cache.RemoveTile(storedRef);
    storedRef = 0;
}
Defensive patterns

Strategy: validation

Validate before calling

if (tileRef == 0) return; // nothing to remove
cache.RemoveTile(tileRef);

Type guard

static bool IsValidTileRef(long r) => r != 0;

Prevention

When it happens

Trigger: Calling RemoveTile(0) directly; passing a ref returned as 0 from a failed AddTile/GetTileRef; removing a tile whose ref was never captured or was reset to default.

Common situations: Unloading a tile from a ref field that defaulted to 0 because load failed earlier; double-unload where the second call still holds the now-zeroed ref; code that assumes GetTileRef always returns a valid handle.

Related errors


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