OrchardCMS/OrchardCore · error · OpenIddictExceptions.ConcurrencyException

The application was concurrently updated and cannot be…

Error message

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

What it means

OpenIdApplicationStore.UpdateAsync saves the OpenIddict application document via YesSql. YesSql detects a version conflict (ConcurrencyException) when another operation updated the same document, and the store translates it into OpenIddictExceptions.ConcurrencyException, following the OpenIddict store contract.

Solutions

  1. Retry the operation: reload the application via FindAsync and re-apply the update, as the message instructs.
  2. Serialize writes to the same application (locking, single writer, or queue) to avoid conflicts.
  3. Reduce write frequency to application documents (avoid persisting on every token flow when possible).

Example fix

// before
await store.UpdateAsync(application, cancellationToken);
// after
try
{
    await store.UpdateAsync(application, cancellationToken);
}
catch (OpenIddictExceptions.ConcurrencyException)
{
    application = await store.FindAsync(application.ClientId, cancellationToken);
    // re-apply changes and retry once
}
Defensive patterns

Strategy: retry

Try / catch

try
{
    await store.UpdateAsync(app, ct);
}
catch (OpenIddictExceptions.ConcurrencyException)
{
    var fresh = await store.FindAsync(app.ClientId, ct);
    // re-apply changes to fresh, retry once with bounded backoff
}

Prevention

When it happens

Trigger: Two concurrent requests updating the same OpenIddict application (e.g. concurrent client edits, token refresh writing to the same application document) causing SaveChangesAsync to fail the optimistic concurrency check.

Common situations: High-concurrency OAuth/OpenID flows hitting the same client; duplicate background jobs processing the same application; users editing the same client in the admin UI from two tabs.

Related errors


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

Appendix: source

Thrown at src/OrchardCore/OrchardCore.OpenId.Core/YesSql/Stores/OpenIdApplicationStore.cs:471

        return default;
    }

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

        cancellationToken.ThrowIfCancellationRequested();

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

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

    /// <inheritdoc/>
    public virtual ValueTask<ImmutableArray<string>> GetRolesAsync(TApplication application, CancellationToken cancellationToken)
    {
        ArgumentNullException.ThrowIfNull(application);

        return ValueTask.FromResult(application.Roles);
    }

    /// <inheritdoc/>
    public virtual IAsyncEnumerable<TApplication> ListInRoleAsync(string role, CancellationToken cancellationToken)
    {
        ArgumentException.ThrowIfNullOrEmpty(role);

View on GitHub (pinned to 4306c0717f)