stride3d/stride · error · NotSupportedException

Legacy (pre-4.2) ServiceWire complex-type serialize is not s

Error message

Legacy (pre-4.2) ServiceWire complex-type serialize is not supported.

What it means

LegacyBinaryFormatterSerializer implements ServiceWire's ISerializer solely to let the Stride CLI read the BinaryFormatter-encoded handshake payload (ServiceSyncInfo) of Stride 4.1's ServiceWire 5.3.4. BinaryFormatter was removed from modern .NET, so outbound (serialize) calls are deliberately unsupported; only deserializing the single ServiceSyncInfo payload is implemented. This NotSupportedException fires whenever the wire protocol attempts to serialize a complex (non-primitive) object through this legacy serializer.

Solutions

  1. Restrict legacy-serializer use to the supported path: only methods whose arguments are ServiceWire primitive types (strings, byte[]) and whose result is byte[], as the GenerateShaderKeys call in LegacyShaderCodeGenerator does.
  2. If you must send complex types, upgrade the target Stride project to 4.2+ so the modern JSON-based ServiceWire serializer (the default NpClient) can be used instead of the legacy shim.
  3. Serialize the payload yourself into primitives (e.g. JSON string or byte[]) before crossing the ServiceWire boundary.
  4. If this error appears during the handshake only, verify the payload direction: the shim only supports deserializing ServiceSyncInfo, never serializing it.

Example fix

// before (legacy serializer, complex argument)
proxy.MyMethod(myComplexDto); // throws NotSupportedException

// after (flatten to primitives)
var json = JsonSerializer.Serialize(myComplexDto);
proxy.MyMethod(json); // travels as a string type-code, no serializer needed
Defensive patterns

Strategy: validation

Validate before calling

// Before invoking a legacy-pipe remote method, assert all parameters are ServiceWire primitives
static void EnsureLegacySafe(object?[] args) {
    foreach (var a in args)
        if (a is not (string or byte[] or int or bool or long or double or null))
            throw new InvalidOperationException(
                $"Argument {a?.GetType().Name} requires complex-type serialization, unsupported on the legacy (pre-4.2) ServiceWire shim.");
}

Type guard

static bool IsLegacySerializable(object? value) =>
    value is null or string or byte[] or int or bool or long or double or float;

Try / catch

try {
    result = proxy.GenerateShaderKeys(name, content);
}
catch (NotSupportedException ex) when (ex.Message.Contains("Legacy (pre-4.2) ServiceWire complex-type serialize")) {
    // fall back to a modern (4.2+) target or flatten the argument to primitives
}

Prevention

When it happens

Trigger: Calling LegacyBinaryFormatterSerializer.Serialize<T>(obj) or Serialize(obj, typeConfigName) — i.e. the ServiceWire NpClient configured with this legacy serializer attempts to send a complex type across the named-pipe channel instead of a primitive type-code.

Common situations: Using the legacy (pre-4.2) serializer combination with a remote method whose parameters are complex types, or invoking any proxy method other than the string/byte[]-based GenerateShaderKeys path the shim was designed for; also发生的 if a custom tool calls the serializer directly.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


AI-assisted analysis of stride3d/stride@96fad776d2 (2026-09-14). Data as JSON: /api/errors/ea788c39521d7c0e. Report an issue: GitHub.

Appendix: source

Thrown at sources/launcher/Stride.Cli/Legacy/LegacyServiceWire.cs:34

// It's the only serializer call on the client's GenerateShaderKeys path, though: the method's string arguments
// and byte[] result travel as ServiceWire primitive type-codes, never through the serializer. So we only need
// to read that one payload, which we do with the safe NRBF reader and rebuild ServiceSyncInfo by hand.
internal sealed class LegacyBinaryFormatterSerializer : ISerializer
{
    public T Deserialize<T>(byte[] bytes)
    {
        if (bytes is null || bytes.Length == 0)
            return default!;
        if (typeof(T) == typeof(ServiceSyncInfo))
            return (T)(object)ReadServiceSyncInfo(bytes);
        throw new NotSupportedException($"Legacy (pre-4.2) ServiceWire deserialize of {typeof(T)} is not supported.");
    }

    public object Deserialize(byte[] bytes, string typeConfigName)
        => throw new NotSupportedException("Legacy (pre-4.2) ServiceWire complex-type deserialize is not supported.");

    public byte[] Serialize<T>(T obj)
        => throw new NotSupportedException("Legacy (pre-4.2) ServiceWire complex-type serialize is not supported.");

    public byte[] Serialize(object obj, string typeConfigName)
        => throw new NotSupportedException("Legacy (pre-4.2) ServiceWire complex-type serialize is not supported.");

    private static ServiceSyncInfo ReadServiceSyncInfo(byte[] bytes)
    {
        var root = (ClassRecord)NrbfDecoder.Decode(new MemoryStream(bytes));

        var methodRecords = ((SZArrayRecord<SerializationRecord>)root.GetArrayRecord(Member(root, "MethodInfos"))).GetArray();
        var methods = new MethodSyncInfo[methodRecords.Length];
        for (var i = 0; i < methodRecords.Length; i++)
        {
            var method = (ClassRecord)methodRecords[i]!;
            methods[i] = new MethodSyncInfo
            {
                MethodIdent = method.GetInt32(Member(method, "MethodIdent")),
                MethodName = method.GetString(Member(method, "MethodName")),
                MethodReturnType = method.GetString(Member(method, "MethodReturnType")),

View on GitHub (pinned to 96fad776d2)