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
- Register a deserialize callback for the missing version via the serializer's Register(DeserializeCallback) / versioned registration API before calling Deserialize.
- 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.
- Verify stream.Position is 0 (or the correct header offset) before calling Deserialize so the version byte is read correctly.
- 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
- Always register deserialize callbacks for every historical version of a type before shipping schema changes.
- Reset stream.Position to 0 before Deserialize.
- Keep serialized data files tagged with the tool/build that produced them.
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
- Serialize callback is invalid.
- Deserialize callback is invalid.
- Try get value callback is invalid.
- No serialize callback registered.
- Serialize callback ' ' is not exist.
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)