egametang/ET · error · Exception
Invalid tile salt
Error message
Invalid tile salt
What it means
RemoveTile decoded a valid index but the salt stored on that tile no longer matches the salt encoded in the ref. The salt increments each time a slot is recycled, so a mismatch means the ref points to a tile that was removed and its slot has since been reused for a different tile. Removing it would corrupt the live tile, so the call is rejected.
Source
Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Detour.TileCache/DtTileCache.cs:303
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;
DtCompressedTile cur = m_posLookup[h];
while (cur != null)
{
if (cur == tile)
{
if (prev != null)
{
prev.next = cur.next;
}
else
{
m_posLookup[h] = cur.next;
}View on GitHub (pinned to 5cab01f7a8)
Solutions
- Zero out your stored ref immediately after a successful RemoveTile and guard against double-removal.
- Resolve the current ref by position (GetTileRef) right before removing rather than trusting a cached handle.
- Treat a salt mismatch as 'already removed' and continue gracefully rather than fatal.
Example fix
// before
cache.RemoveTile(ref);
cache.RemoveTile(ref); // throws: salt changed after first remove
// after
if (ref != 0) { cache.RemoveTile(ref); ref = 0; } Defensive patterns
Strategy: try-catch
Validate before calling
// Resolve fresh; a recycled slot returns a different (or zero) ref long ref = cache.GetTileRef(tx, ty, layer); if (ref == 0) return; // already gone cache.RemoveTile(ref);
Try / catch
try { cache.RemoveTile(ref); storedRef = 0; }
catch (Exception e) when (e.Message.Contains("Invalid tile salt"))
{ storedRef = 0; /* slot was recycled; treat as already removed */ } Prevention
- Clear stored refs immediately after removal.
- Resolve the current ref by position instead of trusting a cached handle.
- Treat salt mismatch as a benign 'already removed' condition.
When it happens
Trigger: Calling RemoveTile twice with the same ref; removing a ref whose tile was already evicted by an obstacle update or streaming unload; holding a ref across a cache rebuild.
Common situations: Double-unload; an async streaming pipeline where the same tile is removed by two code paths; refs captured before a cache.Init/Reset and reused afterward.
Related errors
- Invalid tile index
- Invalid tile ref
- Tile index too high
- Item cannot be null
- MethodHook:targetMethod and replacementMethod and proxyMetho
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/bf8edb8c88098fe7.
Report an issue: GitHub.