egametang/ET · error · IOException
Invalid magic
Error message
Invalid magic
What it means
DtTileCacheReader.Read parses a tile-cache *set* file. The first int must be TILECACHESET_MAGIC ('T','S','E','T' = 0x54534554); if it fails the reader tries byte-swapping once and re-checks, throwing only if both fail. So this error means the bytes are not a tile-cache set in either endianness.
Source
Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Detour.TileCache/Io/DtTileCacheReader.cs:53
_compFactory = compFactory;
}
public DtTileCache Read(BinaryReader @is, int maxVertPerPoly, IDtTileCacheMeshProcess meshProcessor)
{
RcByteBuffer bb = IOUtils.ToByteBuffer(@is);
return Read(bb, maxVertPerPoly, meshProcessor);
}
public DtTileCache Read(RcByteBuffer bb, int maxVertPerPoly, IDtTileCacheMeshProcess meshProcessor)
{
DtTileCacheSetHeader header = new DtTileCacheSetHeader();
header.magic = bb.GetInt();
if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC)
{
header.magic = IOUtils.SwapEndianness(header.magic);
if (header.magic != DtTileCacheSetHeader.TILECACHESET_MAGIC)
{
throw new IOException("Invalid magic");
}
bb.Order(bb.Order() == RcByteOrder.BIG_ENDIAN ? RcByteOrder.LITTLE_ENDIAN : RcByteOrder.BIG_ENDIAN);
}
header.version = bb.GetInt();
if (header.version != DtTileCacheSetHeader.TILECACHESET_VERSION)
{
if (header.version != DtTileCacheSetHeader.TILECACHESET_VERSION_RECAST4J)
{
throw new IOException("Invalid version");
}
}
bool cCompatibility = header.version == DtTileCacheSetHeader.TILECACHESET_VERSION;
header.numTiles = bb.GetInt();
header.meshParams = paramReader.Read(bb);
header.cacheParams = ReadCacheParams(bb, cCompatibility);View on GitHub (pinned to 5cab01f7a8)
Solutions
- Verify the file is a tile-cache set produced by DtTileCacheWriter (magic 'TSET').
- Check the file is not truncated or corrupted (compare length/hash against the baked asset).
- Ensure you are calling the set reader (DtTileCacheReader) only on full tile-cache set files, not individual layers or navmesh tiles.
Example fix
// before: feeding the wrong blob
using var fs = File.OpenRead(path); // might be a navmesh, not a tilecache set
var tc = reader.Read(bb, maxVertPerPoly, proc);
// after: confirm magic before handing off
int magic = bb.GetInt();
if (magic != DtTileCacheSetHeader.TILECACHESET_MAGIC
&& IOUtils.SwapEndianness(magic) != DtTileCacheSetHeader.TILECACHESET_MAGIC)
throw new InvalidDataException("not a tile-cache set file");
bb.RewindInt();
var tc = reader.Read(bb, maxVertPerPoly, proc); Defensive patterns
Strategy: validation
Validate before calling
int magic = bb.GetInt();
if (magic != DtTileCacheSetHeader.TILECACHESET_MAGIC
&& IOUtils.SwapEndianness(magic) != DtTileCacheSetHeader.TILECACHESET_MAGIC)
throw new InvalidDataException("not a tile-cache set file (bad magic)"); Try / catch
try { return reader.Read(bb, maxVertPerPoly, proc); }
catch (IOException e) when (e.Message == "Invalid magic")
{ /* file is not a tile-cache set; do not retry unchanged */ } Prevention
- Only pass full tile-cache set files (magic 'TSET') to DtTileCacheReader.
- Verify file integrity (length/hash) before loading.
- Do not feed single layers or navmesh tiles to the set reader.
When it happens
Trigger: Calling DtTileCacheReader.Read on a file that isn't a tile-cache set (e.g. a raw navmesh, a single layer, or unrelated bytes); a truncated/corrupt header where even the swapped int isn't the magic.
Common situations: Loading the wrong asset (navmesh .bin mistaken for a tile-cache set); corrupt download; feeding a single layer blob to the set reader.
Related errors
AI-assisted analysis of egametang/ET@5cab01f7a8 (2026-08-13).
Data as JSON: /api/errors/f9e2c0fbdde433f0.
Report an issue: GitHub.