egametang/ET · error · IOException

Invalid number of verts per poly {header.maxVertsPerPoly}

Error message

Invalid number of verts per poly {header.maxVertsPerPoly}

What it means

Thrown by DtMeshSetReader.Read when the parsed set header has maxVertsPerPoly <= 0. This value (nvp) defines the max polygon size for the navmesh; a non-positive value is invalid. It usually means the caller passed -1 (the default) but the set file did not embed the field (only NAVMESHSET_VERSION_RECAST4J embeds it).

Source

Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Detour/Io/DtMeshSetReader.cs:65

            return Read(bb, maxVertPerPoly, true);
        }

        public DtNavMesh Read(BinaryReader @is)
        {
            return Read(IOUtils.ToByteBuffer(@is));
        }

        public DtNavMesh Read(RcByteBuffer bb)
        {
            return Read(bb, -1, false);
        }

        DtNavMesh Read(RcByteBuffer bb, int maxVertPerPoly, bool is32Bit)
        {
            NavMeshSetHeader header = ReadHeader(bb, maxVertPerPoly);
            if (header.maxVertsPerPoly <= 0)
            {
                throw new IOException("Invalid number of verts per poly " + header.maxVertsPerPoly);
            }

            bool cCompatibility = header.version == NavMeshSetHeader.NAVMESHSET_VERSION;
            DtNavMesh mesh = new DtNavMesh(header.option, header.maxVertsPerPoly);
            ReadTiles(bb, is32Bit, header, cCompatibility, mesh);
            return mesh;
        }

        private NavMeshSetHeader ReadHeader(RcByteBuffer bb, int maxVertsPerPoly)
        {
            NavMeshSetHeader header = new NavMeshSetHeader();
            header.magic = bb.GetInt();
            if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC)
            {
                header.magic = IOUtils.SwapEndianness(header.magic);
                if (header.magic != NavMeshSetHeader.NAVMESHSET_MAGIC)
                {
                    throw new IOException("Invalid magic " + header.magic);

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Call Read(bb, maxVertPerPoly, is32Bit) with a valid nvp (typically 6, matching DT_VERTS_PER_POLYGON) when the file does not embed it.
  2. Ensure exported set files use a version that embeds maxVertsPerPoly, or always supply it at load time.
  3. Validate the value is > 0 before constructing the DtNavMesh.

Example fix

// before
var mesh = setReader.Read(bb); // default nvp = -1 -> throws

// after
const int NVP = 6;
var mesh = setReader.Read(bb, NVP, false);
Defensive patterns

Strategy: validation

Validate before calling

int nvp = ResolveNvp(); // from config or known good default
if (nvp <= 0) nvp = 6; // DT_VERTS_PER_POLYGON
var mesh = setReader.Read(bb, nvp, false);

Type guard

static bool IsValidNvp(int nvp) => nvp > 0;

Try / catch

try { var mesh = setReader.Read(bb, nvp, is32Bit); }
catch (IOException e) when (e.Message.Contains("verts per poly"))
{ /* supply explicit nvp and retry */ }

Prevention

When it happens

Trigger: Calling Read(bb) with the no-nvp overload on a set file whose version is not NAVMESHSET_VERSION_RECAST4J, so maxVertsPerPoly stays at the default -1; passing maxVertPerPoly <= 0 explicitly.

Common situations: Loading a standard (non-recast4j) navmesh set through the convenience overload that does not supply nvp; misconfiguration of the loader pipeline.

Related errors


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