OrchardCMS/OrchardCore · error · InvalidOperationException
Not a draft version.
Error message
Not a draft version.
What it means
DiscardDraftAsync may only be called on a draft: the item must not be Published and must be the Latest version. Anything else throws InvalidOperationException('Not a draft version.'), protecting published or superseded versions from being discarded.
Solutions
- Load the item with VersionOptions.Latest and verify !Published && Latest before discarding
- For published items use UnpublishAsync instead of DiscardDraftAsync
- Re-fetch a fresh instance if the passed one may be stale
- Guard with an if-check or catch InvalidOperationException to handle race conditions
Example fix
// before
var item = await _contentManager.GetAsync(id, VersionOptions.Published);
await _contentManager.DiscardDraftAsync(item);
// after
var item = await _contentManager.GetAsync(id, VersionOptions.Latest);
if (item is { Published: false, Latest: true })
{
await _contentManager.DiscardDraftAsync(item);
} Defensive patterns
Strategy: type-guard
Validate before calling
var latest = await cm.GetAsync(id, VersionOptions.Latest); bool discardable = latest is { Published: false, Latest: true }; Type guard
bool isDraft = contentItem is { Published: false, Latest: true }; Try / catch
try { await cm.DiscardDraftAsync(item); } catch (InvalidOperationException ex) when (ex.Message == "Not a draft version.") { /* item is published or superseded; reload or unpublish instead */ } Prevention
- Check Published/Latest flags before discarding
- Use UnpublishAsync for published items
- Fetch fresh instances instead of reusing stale ones
When it happens
Trigger: Calling DiscardDraftAsync with a published item (Published=true), an outdated version (Latest=false), or a stale instance superseded by a newer draft/publish.
Common situations: UI code caching a published item and later discarding a 'draft'; concurrent edits where another user published; passing an item from AllVersions query results.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Not the latest version.
- The content item is missing a 'ContentItemId'.
- Content type name contains invalid characters
- Content type name is reserved for internal use
- Content part name must start with a letter
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/20507073572917a9.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore.ContentManagement/DefaultContentManager.cs:955
foreach (var version in activeVersions)
{
version.Published = false;
version.Latest = false;
await _session.SaveAsync(version);
}
await ReversedHandlers.InvokeAsync((handler, context) => handler.RemovedAsync(context), context, _logger);
return true;
}
public async Task DiscardDraftAsync(ContentItem contentItem)
{
ArgumentNullException.ThrowIfNull(contentItem);
if (contentItem.Published || !contentItem.Latest)
{
throw new InvalidOperationException("Not a draft version.");
}
var publishedItem = await GetAsync(contentItem.ContentItemId, VersionOptions.Published);
var context = new RemoveContentContext(contentItem, publishedItem == null);
await Handlers.InvokeAsync((handler, context) => handler.RemovingAsync(context), context, _logger);
contentItem.Latest = false;
await _session.SaveAsync(contentItem);
await ReversedHandlers.InvokeAsync((handler, context) => handler.RemovedAsync(context), context, _logger);
if (publishedItem != null)
{
publishedItem.Latest = true;
await _session.SaveAsync(publishedItem);
}View on GitHub (pinned to 4306c0717f)