stride3d/stride · error · InvalidOperationException

The last chained serializer is invoking non-existing next…

Error message

The last chained serializer is invoking non-existing next serializer

What it means

ChainedSerializer forwards ReadYaml to the next serializer in its chain. This error means the code invoked ReadYaml on a chained serializer that is the LAST link in the chain, so there is no next serializer to delegate to — a broken/incorrectly assembled serializer chain.

Solutions

  1. Check how the ChainedSerializer was registered/constructed and ensure it is appended after a concrete terminal serializer (e.g. ObjectSerializer/PrimitiveSerializer)
  2. Rebuild the serializer chain via the standard SerializerFactory instead of hand-assembling links
  3. Inspect ChainedSerializer.Next before invoking ReadYaml in custom code and handle the null case
  4. Update/fix custom ISerializerFactory plugins that modify the chain ordering

Example fix

// before
var chained = new ChainedSerializer();
chained.ReadYaml(ref objectContext); // throws: chain ends here
// after
var chained = new ChainedSerializer(new ObjectSerializer());
chained.ReadYaml(ref objectContext); // delegates to real serializer
Defensive patterns

Strategy: try-catch

Validate before calling

if (serializer is ChainedSerializer cs && cs.Next == null)
    throw new InvalidOperationException("Chained serializer has no next serializer; assemble the chain first");

Type guard

static bool HasNext(ChainedSerializer s) => s?.Next != null;

Try / catch

try
{
    chained.ReadYaml(ref objectContext);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("non-existing next serializer"))
{
    // fall back to the concrete serializer for this type
}

Prevention

When it happens

Trigger: Calling ReadYaml (directly or via ObjectContext) on a ChainedSerializer that was constructed with no next serializer, or whose chain was mis-assembled so the terminal node is asked to delegate instead of doing real work.

Common situations: Custom serialization setups registering a ChainedSerializer without appending it to a real serializer; serialization service factory changes/upgrades altering chain composition; manually building serializer chains in tests.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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

Appendix: source

Thrown at sources/core/Stride.Core.Yaml/Serialization/Serializers/ChainedSerializer.cs:108

        /// </summary>
        /// <param name="nextSerializer">The serializer to append.</param>
        public void Append([CanBeNull] ChainedSerializer nextSerializer)
        {
            // Update current Next if non-null to target the last of the chain we're appending
            Next?.SetPrev(nextSerializer?.Last);
            nextSerializer?.Last.SetNext(Next);
            // Set the current Next to the given serializer
            Next = nextSerializer;
            // Make sure that the link with the old Prev of the given serializer is cleared
            nextSerializer?.Prev?.SetNext(null);
            // And set the Prev of the given serializer to be this one.
            nextSerializer?.SetPrev(this);
        }

        /// <inheritdoc/>
        public virtual object ReadYaml(ref ObjectContext objectContext)
        {
            if (Next == null) throw new InvalidOperationException("The last chained serializer is invoking non-existing next serializer");
            return Next.ReadYaml(ref objectContext);
        }

        /// <inheritdoc/>
        public virtual void WriteYaml(ref ObjectContext objectContext)
        {
            if (Next == null) throw new InvalidOperationException("The last chained serializer is invoking non-existing next serializer");
            Next.WriteYaml(ref objectContext);
        }

        [NotNull]
        private ChainedSerializer FindBoundary([NotNull] Func<ChainedSerializer, ChainedSerializer> navigate)
        {
            var current = this;
            while (navigate(current) != null)
            {
                current = navigate(current);
            }

View on GitHub (pinned to 96fad776d2)