stride3d/stride · error · InvalidOperationException

Converter is not null but previous Converter was null

Error message

Converter{i} is not null but previous Converter{i - 1} was null

What it means

The Chained value converter applies a fixed-size array of converters in order; a null entry acts as a pass-through and ends the chain. Once a null converter has ended the conversion, any later non-null converter is a configuration error — the chain is ambiguous — so Convert throws this InvalidOperationException.

Solutions

  1. Renumber the converters so all non-null converters occupy a contiguous prefix starting at Converter0
  2. Set the gaps to null consistently: keep converters 0..k non-null and k+1..Max null
  3. Split into two chained converters instead of leaving a hole
  4. Add a unit/preview check that converters form a prefix sequence before use

Example fix

<!-- before -->
<c:Chained Converter="{x:Null}" Converter1="{StaticResource convA}"/>
<!-- after -->
<c:Chained Converter="{StaticResource identity}" Converter1="{StaticResource convA}"/>
Defensive patterns

Strategy: type-guard

Validate before calling

object[] converters = { ch.Converter, ch.Converter1, ch.Converter2 };
bool valid = converters.Select(c => c != null).TakeWhile(b => b).Count() == converters.Count(c => c != null);

Type guard

static bool IsContiguousPrefix(object[] converters) => converters.TakeWhile(c => c != null).Count() == converters.Count(c => c != null);

Try / catch

try { return chained.Convert(value, targetType, param, culture); } catch (InvalidOperationException ex) when (ex.Message.Contains("was null")) { return Binding.DoNothing; }

Prevention

When it happens

Trigger: Declaring a ChainedConverter with ConverterN set but an earlier ConverterN-1 null in XAML (e.g. Converter3 defined while Converter2 is missing). Passing nulls into the converters array via code in a non-prefix layout.

Common situations: Copy-pasting converter XAML and forgetting to shift indices; removing an intermediate converter without renumbering; designers binding only Converter0 and Converter2.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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

Appendix: source

Thrown at sources/presentation/Stride.Core.Presentation.Avalonia/Converters/Chained.cs:263

    }

    /// <inheritdoc/>
    public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
    {
        var output = value;
        var conversionEnded = false;

        for (var i = 0; i < MaxConverterCount; ++i)
        {
            var input = output;
            if (converters[i] == null)
            {
                conversionEnded = true;
                continue;
            }

            if (conversionEnded)
                throw new InvalidOperationException($"Converter{i} is not null but previous Converter{i - 1} was null");

            var type = converterTargetType[i] ?? (i == MaxConverterCount - 1 || converters[i + 1] == null ? targetType : typeof(object));
            output = converters[i]!.Convert(input, type, converterParameters[i], culture);
        }
        return output;
    }

    /// <inheritdoc/>
    public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
    {
        var output = value;

        var conversionStarted = false;

        for (var i = MaxConverterCount - 1; i >= 0; --i)
        {
            var input = output;
            if (converters[i] == null)

View on GitHub (pinned to 96fad776d2)