EllanJiang/GameFramework · error · GameFrameworkException

Deserialize callback

Error message

Deserialize callback '{0}' is not exist.

What it means

GameFrameworkSerializer.Deserialize reads a version byte from the stream and looks up a registered deserialization callback for that version in m_DeserializeCallbacks. When no callback was registered for the version byte found in the stream, it throws this GameFrameworkException instead of silently producing corrupt data. This protects you from deserializing data written by an incompatible serializer version.

Solutions

  1. Register a deserialize callback for the missing version via the serializer's Register(DeserializeCallback) / versioned registration API before calling Deserialize.
  2. Check the version byte reported in the message ('{0}' is the version number) and confirm which side wrote it; align the serializer versions on both writer and reader.
  3. Verify stream.Position is 0 (or the correct header offset) before calling Deserialize so the version byte is read correctly.
  4. Re-export/re-serialize the data with the current serializer version if legacy callbacks cannot be provided.

Example fix

// before
byte[] data = File.ReadAllBytes(path);
var obj = serializer.Deserialize(stream); // throws if version has no callback

// after
serializer.Register(1, DeserializeV1); // register callback for legacy version
stream.Position = 0;
var obj = serializer.Deserialize(stream);
Defensive patterns

Strategy: try-catch

Validate before calling

// before deserializing
if (stream.Position != 0) stream.Position = 0;
int version = stream.PeekByte ?? stream.ReadByte(); // or read into a buffered copy
bool known = serializer.IsVersionRegistered(version); // if such a check API exists
// otherwise: keep a HashSet<byte> of versions you registered and check the first byte of the buffer

Type guard

bool CanDeserialize(byte[] buffer, ISet<byte> registeredVersions) => buffer != null && buffer.Length > 0 && registeredVersions.Contains(buffer[0]);

Try / catch

try
{
    obj = serializer.Deserialize(stream);
}
catch (GameFrameworkException ex) when (ex.Message.Contains("is not exist"))
{
    // log stream origin + version, fall back to migration or re-export path
}

Prevention

When it happens

Trigger: Calling Deserialize(stream) when the stream's leading version byte does not match any version passed to the corresponding Register/Serialize path — e.g. data serialized by a newer or older version of the type, or a callback for that version never registered, or the stream position is wrong so the byte read is not actually the version byte.

Common situations: Persisted save/data files created before the type's serialized layout changed and no legacy deserialize callback was registered; sharing binary data between builds where one side registered extra version callbacks; calling Deserialize on a stream whose read pointer was not rewound to position 0 so a data byte is misread as the version.

Understand the failure class

Background: "cannot parse invalid wire-format data", "cannot unmarshal", "failed unmarshalling": protobuf unmarshal errors explained — this error's family across 10 libraries.

Related errors


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

Appendix: source

Thrown at GameFramework/Base/GameFrameworkSerializer.cs:167

        /// </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>
        /// <returns>是否从指定流获取指定键的值成功。</returns>
        public bool TryGetValue(Stream stream, string key, out object value)
        {
            value = null;
            byte[] header = GetHeader();
            byte header0 = (byte)stream.ReadByte();
            byte header1 = (byte)stream.ReadByte();

View on GitHub (pinned to d0c010b051)