OrchardCMS/OrchardCore · error · OpenIddictExceptions.ConcurrencyException

The scope was concurrently updated and cannot be persisted…

Error message

The scope was concurrently updated and cannot be persisted in its current state.
Reload the scope from the database and retry the operation.

What it means

OpenIdScopeStore.UpdateAsync flushes the YesSql session when persisting an OpenIddict scope. If YesSql raises ConcurrencyException because the scope document was modified by another concurrent writer, it is translated to OpenIddictExceptions.ConcurrencyException as required by OpenIddict.

Solutions

  1. Reload the scope from the database and re-apply the change, then retry.
  2. Coordinate scope edits (avoid simultaneous editors/scripts for the same scope).
  3. Implement retry-once-with-reload logic around scope persistence.
  4. Verify no duplicate jobs or tooling write the same scope concurrently.

Example fix

// before
await store.UpdateAsync(scope, cancellationToken);
// after
try
{
    await store.UpdateAsync(scope, cancellationToken);
}
catch (OpenIddictExceptions.ConcurrencyException)
{
    scope = await store.FindAsync(scope.Id, cancellationToken);
    // re-apply edits and retry
}
Defensive patterns

Strategy: retry

Try / catch

try
{
    await store.UpdateAsync(scope, ct);
}
catch (OpenIddictExceptions.ConcurrencyException)
{
    var fresh = await store.FindAsync(scope.Id, ct);
    // re-apply edits to fresh and retry
}

Prevention

When it happens

Trigger: Two concurrent updates to the same API scope (e.g. two admins editing the same scope in the OpenID admin UI, or a script and the UI updating it simultaneously) causing FlushAsync to fail optimistic concurrency.

Common situations: Infrastructure-as-code scripts racing with manual admin edits; multiple tenants/tools provisioning the same scope; concurrent bulk import of scope definitions.

Related errors


AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13). Data as JSON: /api/errors/2ee7c0843bf108c5. Report an issue: GitHub.

Appendix: source

Thrown at src/OrchardCore/OrchardCore.OpenId.Core/YesSql/Stores/OpenIdScopeStore.cs:333

        return default;
    }

    /// <inheritdoc/>
    public virtual async ValueTask UpdateAsync(TScope scope, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(scope);

        cancellationToken.ThrowIfCancellationRequested();

        await _session.SaveAsync(scope, checkConcurrency: true, collection: OpenIdCollection, cancellationToken: cancellationToken);

        try
        {
            await _session.FlushAsync(cancellationToken);
        }
        catch (ConcurrencyException exception)
        {
            throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder()
                .AppendLine("The scope was concurrently updated and cannot be persisted in its current state.")
                .Append("Reload the scope from the database and retry the operation.")
                .ToString(), exception);
        }
    }
}

View on GitHub (pinned to 4306c0717f)