EllanJiang/GameFramework · error · GameFrameworkException

Header is invalid, need

Error message

Header is invalid, need '{0}{1}{2}', current '{3}{4}{5}'.

What it means

Deserialize reads a 3-byte magic header from the stream and compares it to GetHeader() (the serializer's expected signature bytes). It throws when the bytes in the stream differ from the expected header, formatting both expected and actual header characters into the message. This protects against deserializing data that was not produced by this serializer type.

Solutions

  1. Ensure the stream position is 0 and the data was produced by GameFrameworkSerializer<T>.Serialize for the same T.
  2. Verify the file is a complete, non-empty serialized blob (not truncated or overwritten).
  3. Regenerate the file with the current serializer, or keep a legacy reader that accepts the old header.
  4. Pre-validate the first three bytes against the expected header and show a 'corrupt save' path instead of throwing.

Example fix

// before
var data = serializer.Deserialize(File.OpenRead(path)); // may be wrong file
// after
using (var stream = File.OpenRead(path))
{
    if (stream.Length >= 3 && serializer.CheckHeader(stream)) // or compare GetHeader()
    {
        var data = serializer.Deserialize(stream);
    }
    else
    {
        data = PlayerData.CreateDefault(); // fallback for corrupt/foreign files
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

using (var fs = File.OpenRead(path))
{
    if (fs.Length < 3) { return CreateDefault(); }
    var expected = GetHeaderBytes();
    var actual = new byte[3];
    if (fs.Read(actual, 0, 3) != 3 || !actual.SequenceEqual(expected)) { return CreateDefault(); }
    fs.Position = 0;
    return serializer.Deserialize(fs);
}

Type guard

static bool LooksLikeSerializerBlob(FileStream fs, byte[] expectedHeader) => fs != null && fs.Length >= 3;

Try / catch

try { data = serializer.Deserialize(stream); } catch (GameFrameworkException ex) { log.Warning("Save file header mismatch, using defaults: " + ex.Message); data = PlayerData.CreateDefault(); }

Prevention

When it happens

Trigger: Calling serializer.Deserialize(stream) on a stream whose first three bytes don't match the expected header — e.g. an empty/truncated stream (ReadByte returns -1 cast to byte 255), a file saved by a different serializer type T, or a corrupted/manually edited save file.

Common situations: Loading a save file written by another game/framework version with a changed header; reading a config file with the deserializer that was never serialized by it; corrupted downloads; reading from a stream positioned mid-file rather than at position 0.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of EllanJiang/GameFramework@d0c010b051 (2026-09-15). Data as JSON: /api/errors/a7f391f9c5bb6f8b. Report an issue: GitHub.

Appendix: source

Thrown at GameFramework/Base/GameFrameworkSerializer.cs:160

            }

            return callback(stream, data);
        }

        /// <summary>
        /// 从指定流反序列化数据。
        /// </summary>
        /// <param name="stream">指定流。</param>
        /// <returns>反序列化的数据。</returns>
        public T Deserialize(Stream stream)
        {
            byte[] header = GetHeader();
            byte header0 = (byte)stream.ReadByte();
            byte header1 = (byte)stream.ReadByte();
            byte header2 = (byte)stream.ReadByte();
            if (header0 != header[0] || header1 != header[1] || header2 != header[2])
            {
                throw new GameFrameworkException(Utility.Text.Format("Header is invalid, need '{0}{1}{2}', current '{3}{4}{5}'.", (char)header[0], (char)header[1], (char)header[2], (char)header0, (char)header1, (char)header2));
            }

            byte version = (byte)stream.ReadByte();
            DeserializeCallback callback = null;
            if (!m_DeserializeCallbacks.TryGetValue(version, out callback))
            {
                throw new GameFrameworkException(Utility.Text.Format("Deserialize callback '{0}' is not exist.", version));
            }

            return callback(stream);
        }

        /// <summary>
        /// 尝试从指定流获取指定键的值。
        /// </summary>
        /// <param name="stream">指定流。</param>
        /// <param name="key">指定键。</param>
        /// <param name="value">指定键的值。</param>

View on GitHub (pinned to d0c010b051)