{"record":{"id":"2ee7c0843bf108c5","repo":"OrchardCMS/OrchardCore","slug":"the-scope-was-concurrently-updated-and-cannot-be-persisted","errorCode":null,"errorMessage":"The scope was concurrently updated and cannot be persisted in its current state.\nReload the scope from the database and retry the operation.","messagePattern":"The scope was concurrently updated and cannot be persisted in its current state\\.\nReload the scope from the database and retry the operation\\.","errorType":"exception","errorClass":"OpenIddictExceptions.ConcurrencyException","httpStatus":null,"severity":"error","filePath":"src/OrchardCore/OrchardCore.OpenId.Core/YesSql/Stores/OpenIdScopeStore.cs","lineNumber":333,"sourceCode":"        return default;\n    }\n\n    /// <inheritdoc/>\n    public virtual async ValueTask UpdateAsync(TScope scope, CancellationToken cancellationToken)\n    {\n        ArgumentNullException.ThrowIfNull(scope);\n\n        cancellationToken.ThrowIfCancellationRequested();\n\n        await _session.SaveAsync(scope, checkConcurrency: true, collection: OpenIdCollection, cancellationToken: cancellationToken);\n\n        try\n        {\n            await _session.FlushAsync(cancellationToken);\n        }\n        catch (ConcurrencyException exception)\n        {\n            throw new OpenIddictExceptions.ConcurrencyException(new StringBuilder()\n                .AppendLine(\"The scope was concurrently updated and cannot be persisted in its current state.\")\n                .Append(\"Reload the scope from the database and retry the operation.\")\n                .ToString(), exception);\n        }\n    }\n}\n","sourceCodeStart":315,"sourceCodeEnd":340,"githubUrl":"https://github.com/OrchardCMS/OrchardCore/blob/4306c0717fe573f6fca1b4955909ddab6a192807/src/OrchardCore/OrchardCore.OpenId.Core/YesSql/Stores/OpenIdScopeStore.cs#L315-L340","documentation":"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.","triggerScenarios":"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.","commonSituations":"Infrastructure-as-code scripts racing with manual admin edits; multiple tenants/tools provisioning the same scope; concurrent bulk import of scope definitions.","solutions":["Reload the scope from the database and re-apply the change, then retry.","Coordinate scope edits (avoid simultaneous editors/scripts for the same scope).","Implement retry-once-with-reload logic around scope persistence.","Verify no duplicate jobs or tooling write the same scope concurrently."],"exampleFix":"// before\nawait store.UpdateAsync(scope, cancellationToken);\n// after\ntry\n{\n    await store.UpdateAsync(scope, cancellationToken);\n}\ncatch (OpenIddictExceptions.ConcurrencyException)\n{\n    scope = await store.FindAsync(scope.Id, cancellationToken);\n    // re-apply edits and retry\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"try\n{\n    await store.UpdateAsync(scope, ct);\n}\ncatch (OpenIddictExceptions.ConcurrencyException)\n{\n    var fresh = await store.FindAsync(scope.Id, ct);\n    // re-apply edits to fresh and retry\n}","preventionTips":["Coordinate scope provisioning scripts with manual admin edits.","Reload-and-retry once on concurrency exceptions instead of failing the request.","Ensure scope imports are the single writer during provisioning windows.","Use optimistic-concurrency-aware update helpers for OpenIddict documents."],"tags":["openid","concurrency","yessql"],"backgroundTag":"database-write-failed","analyzedSha":"4306c0717fe573f6fca1b4955909ddab6a192807","analyzedAt":"2026-09-13T17:41:05.024Z","contentChangedAt":"2026-09-13T17:41:05.024Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}