{"record":{"id":"aa844e5a08919d3a","repo":"egametang/ET","slug":"could-not-find-tile","errorCode":null,"errorMessage":"Could not find tile","messagePattern":"Could not find tile","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"Packages/cn.etetet.recast/Scripts/Core/Share/Detour/DtNavMesh.cs","lineNumber":498,"sourceCode":"                availableTiles.RemoveFirst();\n                m_tileCount++;\n            }\n            else\n            {\n                // Try to relocate the tile to specific index with same salt.\n                int tileIndex = DecodePolyIdTile(lastRef);\n                if (tileIndex >= m_maxTiles)\n                {\n                    throw new Exception(\"Tile index too high\");\n                }\n\n                // Try to find the specific tile id from the free list.\n                DtMeshTile target = m_tiles[tileIndex];\n                // Remove from freelist\n                if (!availableTiles.Remove(target))\n                {\n                    // Could not find the correct location.\n                    throw new Exception(\"Could not find tile\");\n                }\n\n                tile = target;\n                // Restore salt.\n                tile.salt = DecodePolyIdSalt(lastRef);\n            }\n\n            tile.data = data;\n            tile.flags = flags;\n            tile.links.Clear();\n            tile.polyLinks = new int[data.polys.Length];\n            Array.Fill(tile.polyLinks, DtNavMesh.DT_NULL_LINK);\n\n            // Insert tile into the position lut.\n            GetTileListByPos(header.x, header.y).Add(tile);\n\n            // Patch header pointers.\n","sourceCodeStart":480,"sourceCodeEnd":516,"githubUrl":"https://github.com/egametang/ET/blob/5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f/Packages/cn.etetet.recast/Scripts/Core/Share/Detour/DtNavMesh.cs#L480-L516","documentation":"Thrown by DtNavMesh.AddTile when restoring a previously-removed tile via a non-zero lastRef. The code decodes the tile index from lastRef, looks up m_tiles[tileIndex], then tries to remove that tile object from the availableTiles free list. If the tile is not on the free list (already reallocated, never freed, or lastRef points at a live tile), AddTile aborts. It indicates the lastRef handle is stale or points to a slot that is not actually free.","triggerScenarios":"Calling AddTile(data, flags, lastRef) with a lastRef obtained from a tile that has since been re-added, re-removed, or was never removed; passing a refs belonging to a different DtNavMesh instance; reusing a stored lastRef across navmesh rebuilds that changed m_maxTiles.","commonSituations":"Streaming tile pipelines that cache a tile's old ref and try to re-add it after the slot was reused; serializing/deserializing navmesh state and feeding a stale lastRef back; calling AddTile with lastRef from RemoveTile but the navmesh was reconstructed in between.","solutions":["Pass lastRef=0 for a fresh add so the navmesh allocates a new slot from the free list instead of relocating.","Discard any cached lastRef after a navmesh rebuild or after the corresponding tile has been re-added by another caller.","Track the live/freed state of refs yourself and only pass a lastRef that was returned by a RemoveTile you performed on the same DtNavMesh instance.","Verify m_tileCount / free-list capacity before re-adding when streaming tiles concurrently."],"exampleFix":"// before\nlong oldRef = mesh.AddTile(data, 0, storedLastRef); // storedLastRef may be stale\n\n// after\nlong oldRef = storedLastRefIsValid\n    ? mesh.AddTile(data, 0, storedLastRef)\n    : mesh.AddTile(data, 0, 0); // fresh allocation","handlingStrategy":"try-catch","validationCode":"// Guard a restore-add: only reuse lastRef if the slot is still free-ish.\nint idx = DtNavMesh.DecodePolyIdTile(lastRef);\nbool safe = idx < mesh.GetMaxTiles() && lastRef != 0 && refStillTracked;\nlong ref2 = mesh.AddTile(data, flags, safe ? lastRef : 0);","typeGuard":"static bool IsValidRestoreRef(DtNavMesh mesh, long lastRef)\n    => lastRef != 0 && DtNavMesh.DecodePolyIdTile(lastRef) < mesh.GetMaxTiles();","tryCatchPattern":"try { mesh.AddTile(data, flags, lastRef); }\ncatch (Exception e) when (e.Message.Contains(\"Could not find tile\"))\n{\n    // lastRef stale -> fall back to a fresh allocation\n    mesh.AddTile(data, flags, 0);\n}","preventionTips":["Never cache lastRef across navmesh rebuilds; clear stored refs on rebuild.","Only pass a lastRef that your own RemoveTile produced on the same DtNavMesh.","In streaming pipelines, prefer lastRef=0 unless you explicitly relocate a slot.","Invalidate entity-held refs whenever their tile is updated."],"tags":["detour","navmesh","tile","streaming","stale-handle"],"backgroundTag":null,"analyzedSha":"5cab01f7a8bee5f49f4781eebe9e2b1c6d7ebe0f","analyzedAt":"2026-08-13T21:10:40.377Z","schemaVersion":2},"datasetVersion":"2026-08-14T00:17:13.853Z"}