{"record":{"id":"c4f9f9a9dc10a8ca","repo":"egametang/ET","slug":"invalid-tile-salt-c4f9f9","errorCode":null,"errorMessage":"Invalid tile salt","messagePattern":"Invalid tile salt","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.recast/Scripts/Core/Share/Detour/DtNavMesh.cs","lineNumber":587,"sourceCode":"        /// @see #addTile\n        public long RemoveTile(long refs)\n        {\n            if (refs == 0)\n            {\n                return 0;\n            }\n\n            int tileIndex = DecodePolyIdTile(refs);\n            int tileSalt = DecodePolyIdSalt(refs);\n            if (tileIndex >= m_maxTiles)\n            {\n                throw new Exception(\"Invalid tile index\");\n            }\n\n            DtMeshTile tile = m_tiles[tileIndex];\n            if (tile.salt != tileSalt)\n            {\n                throw new Exception(\"Invalid tile salt\");\n            }\n\n            // Remove tile from hash lookup.\n            GetTileListByPos(tile.data.header.x, tile.data.header.y).Remove(tile);\n\n            // Remove connections to neighbour tiles.\n            // Create connections with neighbour tiles.\n\n            // Disconnect from other layers in current tile.\n            List<DtMeshTile> nneis = GetTilesAt(tile.data.header.x, tile.data.header.y);\n            foreach (DtMeshTile j in nneis)\n            {\n                if (j == tile)\n                {\n                    continue;\n                }\n\n                UnconnectLinks(j, tile);","sourceCodeStart":569,"sourceCodeEnd":605,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.recast/Scripts/Core/Share/Detour/DtNavMesh.cs#L569-L605","documentation":"Thrown by DtNavMesh.RemoveTile when the tile slot at the decoded index exists but its current salt does not match the salt encoded in refs. The salt is a generation counter bumped each time a slot is reused, so a mismatch means the handle refers to an older (or future) generation of that slot. The tile being pointed at is not the one the caller thinks.","triggerScenarios":"Calling RemoveTile with a refs whose tile was already removed and re-added (salt bumped); using a refs cached before a remove/add cycle; holding a refs across a navmesh rebuild.","commonSituations":"Caching tile refs in game objects for long-lived entities while the streaming system recycles tile slots; navigation queries returning refs that are then held and later used for removal after a tile update.","solutions":["Re-acquire the tile ref immediately before removal instead of caching it long-term.","Invalidate stored refs whenever their tile is removed or updated.","Treat a salt mismatch as 'already gone' and drop the ref rather than retrying.","Reduce tile recycling churn if refs must be held longer (fewer add/remove cycles)."],"exampleFix":"// before\nmesh.RemoveTile(entity.CachedTileRef); // ref may be stale\n\n// after\nlong currentRef = mesh.GetTileRefAt(x, y, layer);\nif (currentRef != 0)\n    mesh.RemoveTile(currentRef);","handlingStrategy":"validation","validationCode":"// Re-fetch the current ref at the tile position before removing.\nlong current = mesh.GetTileRefAt(x, y, layer);\nif (current != 0) mesh.RemoveTile(current);\n// don't trust a long-cached ref","typeGuard":"// Salt cannot be read externally in all ports; treat any cached ref older than the latest add/remove cycle as invalid.\nstatic bool RefIsCurrent(long cached, long current) => cached == current;","tryCatchPattern":"try { mesh.RemoveTile(refs); }\ncatch (Exception e) when (e.Message.Contains(\"Invalid tile salt\"))\n{ // tile already recycled; treat as already removed }","preventionTips":["Re-query the tile ref right before removal rather than caching indefinitely.","Invalidate cached refs on every add/remove cycle for that slot.","Treat salt mismatch as benign 'already gone' and skip.","Minimize tile recycling churn when refs must be held."],"tags":["detour","navmesh","tile","salt","stale-handle"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}