elsa-workflows/elsa-core · error · InvalidOperationException
Adapter ' ' settings migration contains a cycle.
Error message
Adapter '{adapterType}' settings migration contains a cycle. What it means
As a loop guard, MigrateAsync caps migration steps at 64. Since every valid migration strictly increases the version, exceeding 64 steps can only happen if the migration graph loops (or the invariant check failed), so the service aborts instead of spinning forever.
Solutions
- Inspect the registered migrations for the adapter and remove/repair the cycle.
- If the chain is legitimately long, split the migration or raise the step cap consciously.
- Re-register a clean, strictly-increasing migration set at startup.
Example fix
// before // cycles 1->2, 2->1 // after // chain 1->2, 2->3 (strictly increasing)
Defensive patterns
Strategy: try-catch
Try / catch
try { await service.MigrateAsync(type, v, settings); }
catch (InvalidOperationException ex) when (ex.Message.Contains("contains a cycle"))
{ /* dump registered migration graph for diagnosis; abort migration */ } Prevention
- Keep the migration registry immutable after startup
- Enforce strictly increasing versions so cycles are impossible
- Add a graph-cycle unit test for each adapter's migrations
When it happens
Trigger: A migration graph where (adapterType, version) lookups cycle between versions — normally prevented by the ToVersion > version check, so hitting this indicates corrupted registration state or a mutated registry during execution.
Common situations: Custom/buggy registry implementations that return inconsistent migrations between iterations; manually composed migration chains with duplicate overlapping steps; extremely long legitimate chains (>64 versions) in heavily-iterated adapters.
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
- Settings version is not compatible with adapter ' ' version…
- Adapter ' ' does not provide a settings migration from…
- Adapter ' ' has an invalid settings migration from version…
- OpenID Connect settings must be an object.
- The adapter type ' ' is not installed or deployment-allowed.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/91c5092969d25a54.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.ExternalAuthentication/Services/AdapterSettingsMigrationService.cs:44
throw new InvalidOperationException($"The adapter type '{adapterType}' is not installed or deployment-allowed.");
var currentVersion = adapter.Describe().SettingsVersion;
if (settingsVersion <= 0 || settingsVersion > currentVersion)
throw new InvalidOperationException($"Settings version {settingsVersion} is not compatible with adapter '{adapterType}' version {currentVersion}.");
if (settingsVersion == currentVersion)
return new(currentVersion, settings.Clone(), false);
var migrated = settings.Clone();
var version = settingsVersion;
var stepCount = 0;
while (version < currentVersion)
{
if (!_migrations.TryGetValue((adapterType, version), out var migration))
throw new InvalidOperationException($"Adapter '{adapterType}' does not provide a settings migration from version {version}.");
if (migration.ToVersion <= version || migration.ToVersion > currentVersion)
throw new InvalidOperationException($"Adapter '{adapterType}' has an invalid settings migration from version {version} to {migration.ToVersion}.");
if (++stepCount > 64)
throw new InvalidOperationException($"Adapter '{adapterType}' settings migration contains a cycle.");
migrated = (await migration.MigrateAsync(migrated, cancellationToken)).Clone();
version = migration.ToVersion;
}
return new(version, migrated, true);
}
private static IReadOnlyDictionary<(string AdapterType, int FromVersion), IAdapterSettingsMigration> BuildMigrationIndex(
IEnumerable<IAdapterSettingsMigration> migrations)
{
var result = new Dictionary<(string AdapterType, int FromVersion), IAdapterSettingsMigration>();
foreach (var migration in migrations)
{
if (string.IsNullOrWhiteSpace(migration.AdapterType) || migration.FromVersion <= 0)
throw new InvalidOperationException("Adapter settings migrations must define an adapter type and a positive source version.");
if (!result.TryAdd((migration.AdapterType, migration.FromVersion), migration))
throw new InvalidOperationException($"Adapter '{migration.AdapterType}' registers more than one migration from version {migration.FromVersion}.");View on GitHub (pinned to fe9217bdfa)