egametang/ET · error · Exception

invalid level: {level} (expected: {LEVEL_1} or {LEVEL_2})

Error message

invalid level: {level} (expected: {LEVEL_1} or {LEVEL_2})

What it means

FastLZ.Decompress reads a 3-bit compression level from the first byte of the input (bits 5-7, plus 1). The only valid levels are LEVEL_1 and LEVEL_2. If the derived level is neither, the input is not a valid FastLZ-compressed block — it is corrupted, truncated, or produced by a different algorithm/version.

Source

Thrown at Packages/cn.etetet.recast/Scripts/Core/Share/Core/Compression/FastLZ.cs:517

            return op;
        }

        /**
     * Decompress a block of compressed data and returns the size of the decompressed block.
     * If error occurs, e.g. the compressed data is corrupted or the output buffer is not large
     * enough, then 0 (zero) will be returned instead.
     *
     * Decompression is memory safe and guaranteed not to write the output buffer
     * more than what is specified in outLength.
     */
        public static int Decompress(byte[] input, int inOffset, int inLength,
            byte[] output, int outOffset, int outLength)
        {
            //int level = ((*(const flzuint8*)input) >> 5) + 1;
            int level = (input[inOffset] >> 5) + 1;
            if (level != LEVEL_1 && level != LEVEL_2)
            {
                throw new Exception($"invalid level: {level} (expected: {LEVEL_1} or {LEVEL_2})");
            }

            // const flzuint8* ip = (const flzuint8*) input;
            int ip = 0;
            // flzuint8* op = (flzuint8*) output;
            int op = 0;
            // flzuint32 ctrl = (*ip++) & 31;
            long ctrl = input[inOffset + ip++] & 31;

            int loop = 1;
            do
            {
                //  const flzuint8* refs = op;
                int refs = op;
                // flzuint32 len = ctrl >> 5;
                long len = ctrl >> 5;
                // flzuint32 ofs = (ctrl & 31) << 8;
                long ofs = (ctrl & 31) << 8;

View on GitHub (pinned to 5cab01f7a8)

Solutions

  1. Verify the data source: confirm it was produced by FastLZ.Compress and not another compressor.
  2. Check inOffset points to the true start of the FastLZ block (not mid-buffer).
  3. If data is from a cache file, clear/regenerate the cache to rule out corruption.
  4. Add a length/header sanity check on input before calling Decompress.

Example fix

// before
FastLZ.Decompress(data, 0, data.Length, outBuf, 0, outBuf.Length);

// after — validate first byte before decompressing
int lvl = (data[0] >> 5) + 1;
if (lvl != FastLZ.LEVEL_1 && lvl != FastLZ.LEVEL_2)
{
    throw new InvalidOperationException("input is not FastLZ data");
}
FastLZ.Decompress(data, 0, data.Length, outBuf, 0, outBuf.Length);
Defensive patterns

Strategy: validation

Validate before calling

int lvl = (input[inOffset] >> 5) + 1;
if (lvl != FastLZ.LEVEL_1 && lvl != FastLZ.LEVEL_2) throw new InvalidOperationException("not FastLZ data");

Try / catch

try { FastLZ.Decompress(input, inOffset, inLength, output, outOffset, outLength); }
catch (Exception ex) { Log.Error($"FastLZ decompress failed, data may be corrupt: {ex}"); /* regenerate cache */ }

Prevention

When it happens

Trigger: Passing a byte array that was not compressed by FastLZ; passing data compressed by FastLZ level 2 to a reader expecting level 1 (only if the header byte is invalid); a truncated or bit-flipped compressed stream whose first byte is wrong; using the wrong offset so the header byte read is not the FastLZ header.

Common situations: Corrupted cached navmesh/voxel data on disk; a version change in the compression format; reading past the wrong offset; data produced by a different LZ codec (LZ4, zlib) being fed to FastLZ.Decompress.

Related errors


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