{"record":{"id":"ac2c9a10e873399d","repo":"elsa-workflows/elsa-core","slug":"secret-secret-name-is-revoked-and-cannot-be-rotated","errorCode":null,"errorMessage":"Secret '{secret.Name}' is revoked and cannot be rotated.","messagePattern":"Secret '(.+?)' is revoked and cannot be rotated\\.","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"src/modules/Elsa.Secrets/Services/DefaultSecretManager.cs","lineNumber":66,"sourceCode":"    }\n\n    public async Task<Secret> UpdateAsync(string name, UpdateSecretRequest request, CancellationToken cancellationToken = default)\n    {\n        var secret = await GetExistingAsync(name, cancellationToken);\n\n        secret.DisplayName = string.IsNullOrWhiteSpace(request.DisplayName) ? secret.Name : request.DisplayName.Trim();\n        secret.Description = string.IsNullOrWhiteSpace(request.Description) ? null : request.Description.Trim();\n        secret.UpdatedAt = DateTimeOffset.UtcNow;\n\n        await repository.SaveAsync(secret, cancellationToken);\n        return secret;\n    }\n\n    public async Task<Secret> RotateAsync(string name, RotateSecretRequest request, CancellationToken cancellationToken = default)\n    {\n        var secret = await GetExistingAsync(name, cancellationToken);\n        if (secret.Status == SecretStatus.Revoked)\n            throw new InvalidOperationException($\"Secret '{secret.Name}' is revoked and cannot be rotated.\");\n\n        var provider = typeRegistry.Get(secret.TypeName);\n        if (!provider.ValidateRotation(request, secret.StoreName, out var error))\n            throw new InvalidOperationException(error);\n\n        var store = storeRegistry.Get(secret.StoreName);\n        EnsureCanWrite(store);\n        var nextVersion = secret.Versions.Count == 0 ? 1 : secret.Versions.Max(x => x.Version) + 1;\n        var version = new SecretVersion { Version = nextVersion, ExpiresAt = request.ExpiresAt };\n        version.Payload = await store.WriteAsync(secret, version, CreatePayload(request), cancellationToken);\n\n        foreach (var activeVersion in secret.Versions.Where(x => x.Status == SecretStatus.Active))\n            activeVersion.Status = SecretStatus.Retired;\n\n        secret.Versions.Add(version);\n        secret.Status = SecretStatus.Active;\n        secret.UpdatedAt = DateTimeOffset.UtcNow;\n        await repository.SaveAsync(secret, cancellationToken);","sourceCodeStart":48,"sourceCodeEnd":84,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/modules/Elsa.Secrets/Services/DefaultSecretManager.cs#L48-L84","documentation":"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.","triggerScenarios":"Calling ISecretManager.RotateAsync(name, request) for a secret previously revoked via RevokeAsync (Status == SecretStatus.Revoked).","commonSituations":"Automated rotation jobs that do not check status and encounter secrets revoked after a security incident or decommissioning; dashboards offering rotate on revoked secrets.","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."],"exampleFix":"// before\nawait manager.RotateAsync(name, rotateRequest);\n// after\nvar secret = await manager.GetAsync(name);\nif (secret is { Status: SecretStatus.Active })\n    await manager.RotateAsync(name, rotateRequest);\nelse\n    logger.LogInformation(\"Skipping rotation; secret {Name} is not active.\", name);","handlingStrategy":"validation","validationCode":"var secret = await manager.GetAsync(name);\nbool canRotate = secret is { Status: SecretStatus.Active };","typeGuard":"bool IsRotatable(Secret? s) => s is { Status: SecretStatus.Active };","tryCatchPattern":"try { await manager.RotateAsync(name, request); }\ncatch (InvalidOperationException e) when (e.Message.Contains(\"is revoked\")) { /* skip or create replacement secret */ }","preventionTips":["Check Status before lifecycle operations.","Exclude revoked secrets from rotation jobs via ListAsync status filters.","Never offer rotate actions for revoked secrets in tooling."],"tags":["secrets","rotation","invalid-state","lifecycle"],"backgroundTag":"invalid-state-transition","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}