elsa-workflows/elsa-core · error · InvalidOperationException
Secret ' ' is revoked and cannot be rotated.
Error message
Secret '{secret.Name}' is revoked and cannot be rotated. What it means
Thrown by DefaultSecretManager.RotateAsync when the target secret's status is Revoked. Revocation is terminal in this model: a revoked secret cannot receive new versions, so any rotation attempt is rejected with this InvalidOperationException.
Solutions
- Check secret.Status before rotating and skip (or surface as blocked) when it is Revoked.
- If the secret must live again, create a new secret with CreateAsync (or a new name) rather than rotating the revoked one.
- Filter rotation jobs to Status == Active using ListAsync with a status filter.
- Catch InvalidOperationException and log a clear skip/recreate action.
Example fix
// before
await manager.RotateAsync(name, rotateRequest);
// after
var secret = await manager.GetAsync(name);
if (secret is { Status: SecretStatus.Active })
await manager.RotateAsync(name, rotateRequest);
else
logger.LogInformation("Skipping rotation; secret {Name} is not active.", name); Defensive patterns
Strategy: validation
Validate before calling
var secret = await manager.GetAsync(name);
bool canRotate = secret is { Status: SecretStatus.Active }; Type guard
bool IsRotatable(Secret? s) => s is { Status: SecretStatus.Active }; Try / catch
try { await manager.RotateAsync(name, request); }
catch (InvalidOperationException e) when (e.Message.Contains("is revoked")) { /* skip or create replacement secret */ } Prevention
- Check Status before lifecycle operations.
- Exclude revoked secrets from rotation jobs via ListAsync status filters.
- Never offer rotate actions for revoked secrets in tooling.
When it happens
Trigger: Calling ISecretManager.RotateAsync(name, request) for a secret previously revoked via RevokeAsync (Status == SecretStatus.Revoked).
Common situations: Automated rotation jobs that do not check status and encounter secrets revoked after a security incident or decommissioning; dashboards offering rotate on revoked secrets.
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
- The configured secret binding is not active.
- {error}
- Secret ' ' is not active.
- Build() must be called before accessing services
- BPMN element ' ' binds process activity ' ' as the work of…
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/ac2c9a10e873399d.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Secrets/Services/DefaultSecretManager.cs:66
}
public async Task<Secret> UpdateAsync(string name, UpdateSecretRequest request, CancellationToken cancellationToken = default)
{
var secret = await GetExistingAsync(name, cancellationToken);
secret.DisplayName = string.IsNullOrWhiteSpace(request.DisplayName) ? secret.Name : request.DisplayName.Trim();
secret.Description = string.IsNullOrWhiteSpace(request.Description) ? null : request.Description.Trim();
secret.UpdatedAt = DateTimeOffset.UtcNow;
await repository.SaveAsync(secret, cancellationToken);
return secret;
}
public async Task<Secret> RotateAsync(string name, RotateSecretRequest request, CancellationToken cancellationToken = default)
{
var secret = await GetExistingAsync(name, cancellationToken);
if (secret.Status == SecretStatus.Revoked)
throw new InvalidOperationException($"Secret '{secret.Name}' is revoked and cannot be rotated.");
var provider = typeRegistry.Get(secret.TypeName);
if (!provider.ValidateRotation(request, secret.StoreName, out var error))
throw new InvalidOperationException(error);
var store = storeRegistry.Get(secret.StoreName);
EnsureCanWrite(store);
var nextVersion = secret.Versions.Count == 0 ? 1 : secret.Versions.Max(x => x.Version) + 1;
var version = new SecretVersion { Version = nextVersion, ExpiresAt = request.ExpiresAt };
version.Payload = await store.WriteAsync(secret, version, CreatePayload(request), cancellationToken);
foreach (var activeVersion in secret.Versions.Where(x => x.Status == SecretStatus.Active))
activeVersion.Status = SecretStatus.Retired;
secret.Versions.Add(version);
secret.Status = SecretStatus.Active;
secret.UpdatedAt = DateTimeOffset.UtcNow;
await repository.SaveAsync(secret, cancellationToken);View on GitHub (pinned to fe9217bdfa)