stride3d/stride · error · InvalidOperationException

No upgrader for asset type [{0}] allow to reach version {1}

Error message

No upgrader for asset type [{0}] allow to reach version {1}

What it means

AssetUpgraderCollection.Validate walks the registered upgrader ranges from the earliest version, repeatedly advancing to the highest reachable Target, and verifies the chain terminates exactly at currentVersion. If the chain of upgraders for this asset type cannot reach the current version (a gap in the version graph), it throws InvalidOperationException reporting the asset type and the version that could not be reached.

Solutions

  1. Add (or restore) an upgrader covering the missing range so the chain reaches currentVersion.
  2. Fix start/target boundaries of existing upgraders so consecutive ranges connect without gaps.
  3. Ensure the assembly/attribute declaring the missing upgrader is actually registered/loaded.
  4. If the asset no longer supports old versions, remove the asset type or reject old assets instead of pretending a chain exists.

Example fix

// before (gap)
RegisterUpgrader(typeof(U1), v(1,0,0,0), v(2,0,0,0));
RegisterUpgrader(typeof(U2), v(3,0,0,0), v(4,0,0,0));
// after
RegisterUpgrader(typeof(U1), v(1,0,0,0), v(2,0,0,0));
RegisterUpgrader(typeof(U1b), v(2,0,0,0), v(3,0,0,0)); // closes the gap
RegisterUpgrader(typeof(U2), v(3,0,0,0), v(4,0,0,0));
Defensive patterns

Strategy: validation

Validate before calling

// After registration, Validate() itself is the guard; call it at startup
collection.Validate(); // throws early if the upgrade chain has gaps

Type guard

static bool ChainReachesCurrent(IEnumerable<VersionRange> ranges, PackageVersion current)
{
    var v = ranges.Min(r => r.Start);
    foreach (var r in ranges.OrderBy(r => r.Start))
        if (r.Contains(v)) v = r.Target;
    return v == current;
}

Try / catch

try
{
    collection.Validate();
}
catch (InvalidOperationException ex)
{
    logger.Error($"Upgrade chain incomplete: {ex.Message}. Register the missing upgrader range.");
}

Prevention

When it happens

Trigger: Loading/validating a package whose asset type has upgraders registered with a gap, e.g. ranges (1.0→2.0) and (3.0→4.0) with nothing covering 2.0→3.0, so the iteration stops before currentVersion and version != currentVersion.

Common situations: Removing an upgrader for an intermediate asset version that older assets still need; a broken upgrade chain after version bumps where consecutive upgraders' ranges don't abut; plugin/assembly providing an upgrader for the missing range not loaded.

Understand the failure class

Background: "not installed", "pip install", "required for": how missing-dependency errors surface across open-source libraries — this error's family across 34 libraries.

Related errors


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

Appendix: source

Thrown at sources/assets/Stride.Core.Assets/AssetUpgraderCollection.cs:82

    }

    internal void Validate(PackageVersion minVersion)
    {
        lock (upgraders)
        {
            var version = minVersion;
            foreach (var upgrader in upgraders)
            {
                if (!upgrader.Key.Contains(version))
                    continue;

                version = upgrader.Key.Target;
                if (version == currentVersion)
                    break;
            }

            if (version != currentVersion)
                throw new InvalidOperationException("No upgrader for asset type [{0}] allow to reach version {1}".ToFormat(AssetType.Name, currentVersion));
        }
    }

    public IAssetUpgrader GetUpgrader(PackageVersion initialVersion, out PackageVersion targetVersion)
    {
        lock (upgraders)
        {
            var upgrader = upgraders.FirstOrDefault(x => x.Key.Contains(initialVersion));
            if (upgrader.Value == null)
                throw new InvalidOperationException("No upgrader found for version {0} of asset type [{1}]".ToFormat(currentVersion, AssetType.Name));
            targetVersion = upgrader.Key.Target;

            if (!instances.TryGetValue(upgrader.Value, out var result))
            {
                // Cache the upgrader instances
                result = (IAssetUpgrader)Activator.CreateInstance(upgrader.Value)!;
                instances.Add(upgrader.Value, result);
            }

View on GitHub (pinned to 96fad776d2)