egametang/ET · error · IOException

Invalid version {header.version}

Error message

Invalid version {header.version}

What it means

Thrown by DtMeshDataReader.Read when the mesh header version falls outside the supported recast4j range [DT_NAVMESH_VERSION_RECAST4J_FIRST, DT_NAVMESH_VERSION_RECAST4J_LAST] and is not the current DT_NAVMESH_VERSION. The file was written by a Detour/recast4j version this reader cannot interpret.

Source

Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Detour/Io/DtMeshDataReader.cs:73

            header.magic = buf.GetInt();
            if (header.magic != DtMeshHeader.DT_NAVMESH_MAGIC)
            {
                header.magic = IOUtils.SwapEndianness(header.magic);
                if (header.magic != DtMeshHeader.DT_NAVMESH_MAGIC)
                {
                    throw new IOException("Invalid magic");
                }

                buf.Order(buf.Order() == RcByteOrder.BIG_ENDIAN ? RcByteOrder.LITTLE_ENDIAN : RcByteOrder.BIG_ENDIAN);
            }

            header.version = buf.GetInt();
            if (header.version != DtMeshHeader.DT_NAVMESH_VERSION)
            {
                if (header.version < DtMeshHeader.DT_NAVMESH_VERSION_RECAST4J_FIRST
                    || header.version > DtMeshHeader.DT_NAVMESH_VERSION_RECAST4J_LAST)
                {
                    throw new IOException("Invalid version " + header.version);
                }
            }

            bool cCompatibility = header.version == DtMeshHeader.DT_NAVMESH_VERSION;
            header.x = buf.GetInt();
            header.y = buf.GetInt();
            header.layer = buf.GetInt();
            header.userId = buf.GetInt();
            header.polyCount = buf.GetInt();
            header.vertCount = buf.GetInt();
            header.maxLinkCount = buf.GetInt();
            header.detailMeshCount = buf.GetInt();
            header.detailVertCount = buf.GetInt();
            header.detailTriCount = buf.GetInt();
            header.bvNodeCount = buf.GetInt();
            header.offMeshConCount = buf.GetInt();
            header.offMeshBase = buf.GetInt();
            header.walkableHeight = buf.GetFloat();

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Re-bake/re-export navmesh data with the same DotRecast version the runtime uses.
  2. Pin the asset-producing tool and the runtime to a compatible Detour version.
  3. Check DtMeshHeader.DT_NAVMESH_VERSION against the version in the file before loading.

Example fix

// before
var data = meshDataReader.Read(buf, nvp, is32Bit); // throws on old asset

// after
// re-export asset with current toolchain, then:
var data = meshDataReader.Read(buf, nvp, is32Bit);
Defensive patterns

Strategy: validation

Validate before calling

int v = BitConverter.ToInt32(chunk, 4);
bool ok = v == DtMeshHeader.DT_NAVMESH_VERSION ||
          (v >= DtMeshHeader.DT_NAVMESH_VERSION_RECAST4J_FIRST &&
           v <= DtMeshHeader.DT_NAVMESH_VERSION_RECAST4J_LAST);
if (!ok) throw new InvalidDataException($"Unsupported mesh version {v}");

Type guard

static bool IsSupportedMeshVersion(int v)
    => v == DtMeshHeader.DT_NAVMESH_VERSION ||
       (v >= DtMeshHeader.DT_NAVMESH_VERSION_RECAST4J_FIRST &&
        v <= DtMeshHeader.DT_NAVMESH_VERSION_RECAST4J_LAST);

Try / catch

try { var data = meshDataReader.Read(buf, nvp, is32Bit); }
catch (IOException e) when (e.Message.Contains("Invalid version"))
{ /* re-bake asset with matching toolchain */ }

Prevention

When it happens

Trigger: Loading a tile mesh generated by a newer or older recast4j/DotRecast build than the runtime library; mixing outputs from different Detour forks.

Common situations: Updating the DotRecast package version after baking navmesh assets with the old version; consuming assets produced by a different Detour implementation.

Related errors


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