stride3d/stride · error · InvalidOperationException
ServiceWire handshake payload is missing member '{propertyNa
Error message
ServiceWire handshake payload is missing member '{propertyName}'. What it means
ReadServiceSyncInfo decodes Stride 4.1's BinaryFormatter-produced handshake payload with the safe NRBF reader and rebuilds ServiceSyncInfo by matching member names — including auto-property backing fields serialized as "<Name>k__BackingField". When none of the payload's member names equals or contains the expected "<PropertyName>" pattern, Member throws this InvalidOperationException, meaning the decoded handshake record does not have the shape the shim expects.
Solutions
- Verify the target Stride project's version really is < 4.2; for 4.2+ the legacy serializer should not be installed at all (LegacyShaderCodeGenerator picks it only for version < 4.2).
- Dump record.MemberNames for the received payload to see the actual field names and update the Member() name-matching accordingly if ServiceWire renamed fields.
- Check that the payload is uncompressed BinaryFormatter NRBF bytes (legacy wire used no compression); a compressor mismatch produces garbage members.
- Re-verify the byte stream is a complete handshake message, not truncated mid-serialization.
Example fix
// before (strict single-name match fails on renamed fields)
name => name == propertyName || name.Contains($"<{propertyName}>")
// after (case-insensitive tolerant match while debugging)
name => string.Equals(name, propertyName, StringComparison.OrdinalIgnoreCase) || name.Contains($"<{propertyName}>", StringComparison.OrdinalIgnoreCase) Defensive patterns
Strategy: validation
Validate before calling
// Validate the handshake payload shape before trusting it
static bool LooksLikeLegacyServiceSyncInfo(ClassRecord root) {
var names = root.MemberNames.ToHashSet(StringComparer.OrdinalIgnoreCase);
return names.Contains("MethodInfos") && names.Contains("ServiceKeyIndex");
} Type guard
static bool IsBinaryFormatterPayload(byte[] bytes) =>
bytes.Length > 0 && bytes[0] == 0; // NRBF/BinaryFormatter streams start with the serialization header record; JSON payloads do not Try / catch
try {
handshake = ReadServiceSyncInfo(bytes);
}
catch (InvalidOperationException ex) when (ex.Message.StartsWith("ServiceWire handshake payload is missing member")) {
log.LogError($"Handshake decode failed: {ex.Message}. Target is probably not Stride <4.2 or the wire format changed.");
throw;
} Prevention
- Only install LegacyBinaryFormatterSerializer when the server version is strictly < 4.2.
- Log record.MemberNames on failure to diagnose protocol/version drift quickly.
- Keep the legacy wire uncompressed (LegacyDoNothingCompressor); any compression mismatch corrupts the decoded members.
When it happens
Trigger: NrbfDecoder.Decode succeeds on the handshake bytes but the resulting ClassRecord lacks a member named e.g. 'MethodInfos', 'ServiceKeyIndex', 'MethodName', etc. — Member(record, propertyName) finds no FirstOrDefault match.
Common situations: The remote endpoint is not actually Stride 4.1 ServiceWire 5.3.4 (version mismatch — e.g. a 4.2+ server's JSON-encoded payload fed to the legacy decoder); a corrupted or truncated handshake payload; a ServiceWire version whose ServiceSyncInfo field names changed; compression applied that the LegacyDoNothingCompressor did not strip.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- Multiple identifiable objects with the same id {identifiable
- Unable to decode asset part reference [{0}]. Expecting an EN
- Unable to deserialize reference: [{fromScalar.Value}]
- Unable to find class from tag [{0}]
- Unable to find property [{0}] in class [{1}]
AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14).
Data as JSON: /api/errors/5a4deb65f1ec5764.
Report an issue: GitHub.
Appendix: source
Thrown at sources/launcher/Stride.Cli/Legacy/LegacyServiceWire.cs:69
MethodName = method.GetString(Member(method, "MethodName")),
MethodReturnType = method.GetString(Member(method, "MethodReturnType")),
ParameterTypes = ((SZArrayRecord<string>)method.GetArrayRecord(Member(method, "ParameterTypes"))).GetArray()!,
};
}
return new ServiceSyncInfo
{
ServiceKeyIndex = root.GetInt32(Member(root, "ServiceKeyIndex")),
UseCompression = root.GetBoolean(Member(root, "UseCompression")),
CompressionThreshold = root.GetInt32(Member(root, "CompressionThreshold")),
MethodInfos = methods,
};
}
// BinaryFormatter serializes auto-property backing fields ("<Name>k__BackingField"), so match by property name.
private static string Member(ClassRecord record, string propertyName)
=> record.MemberNames.FirstOrDefault(name => name == propertyName || name.Contains($"<{propertyName}>"))
?? throw new InvalidOperationException($"ServiceWire handshake payload is missing member '{propertyName}'.");
}
// Stride 4.1's ServiceWire (5.3.4) did not compress the wire; pair it with the BinaryFormatter serializer above.
internal sealed class LegacyDoNothingCompressor : ICompressor
{
public byte[] Compress(byte[] data) => data;
public byte[] DeCompress(byte[] compressedBytes) => compressedBytes;
}
View on GitHub (pinned to 96fad776d2)