elsa-workflows/elsa-core · error · InvalidOperationException
A value is required for encrypted secrets.
Error message
A value is required for encrypted secrets.
What it means
EncryptedSecretStore protects the secret value with ASP.NET DataProtection before persisting it, so a payload with a null Value cannot be encrypted. WriteAsync throws an InvalidOperationException when payload.Value is null. Only the value is protected; metadata is copied alongside the protected blob.
Solutions
- Ensure payload.Value is populated before writing to the encrypted store.
- For metadata-only updates, read the existing payload, retain/copy its protected value metadata, or re-supply the value.
- Validate in your service layer that Value is non-null when targeting encrypted stores.
Example fix
// before
var payload = new SecretPayload();
payload.Metadata["Environment"] = "Production";
await encryptedStore.WriteAsync(secret, version, payload, ct);
// after
var payload = new SecretPayload { Value = "s3cr3t" };
payload.Metadata["Environment"] = "Production";
await encryptedStore.WriteAsync(secret, version, payload, ct); Defensive patterns
Strategy: validation
Validate before calling
if (payload.Value is null)
throw new ArgumentException("Encrypted secret writes require a non-null Value."); Type guard
bool IsWritable(SecretPayload p) => p.Value is not null;
Try / catch
try
{
await encryptedStore.WriteAsync(secret, version, payload, ct);
}
catch (InvalidOperationException ex) when (ex.Message.Contains("value is required"))
{
logger.LogError("Cannot write secret {SecretId}: value missing.", secret.Id);
} Prevention
- Never create SecretPayload instances without a value when targeting the encrypted store.
- For metadata-only updates, load the current payload and preserve its protected value.
- Validate payloads in your abstraction layer before calling the store.
When it happens
Trigger: Calling WriteAsync on EncryptedSecretStore with a SecretPayload whose Value property is null — e.g., a metadata-only update written through the encrypted store.
Common situations: Update flows that only change metadata and leave Value null; deserialization bugs producing a null value; clients written against ConfigurationSecretStore semantics (where Value may be optional) reused against the encrypted store.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- A configuration key is required.
- Configuration connection
- Elsa Secrets encryption key is not configured. Configure…
- Elsa Secrets encryption key must be exactly 16, 24, or 32…
- {error}
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/e277de077ff31580.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Secrets/Stores/EncryptedSecretStore.cs:19
namespace Elsa.Secrets.Stores;
public class EncryptedSecretStore(ISecretValueProtector protector) : ISecretStore
{
private const string ProtectedValueKey = "protectedValue";
public string Name => SecretStoreNames.Encrypted;
public SecretStoreDescriptor Descriptor { get; } = new(
SecretStoreNames.Encrypted,
"Elsa Encrypted Store",
"Stores values in Elsa-managed encrypted payloads.",
SecretStoreCapabilities.Read | SecretStoreCapabilities.Write | SecretStoreCapabilities.Delete | SecretStoreCapabilities.Test | SecretStoreCapabilities.Versioned,
false);
public Task<SecretPayload> WriteAsync(Secret secret, SecretVersion version, SecretPayload payload, CancellationToken cancellationToken = default)
{
if (payload.Value == null)
throw new InvalidOperationException("A value is required for encrypted secrets.");
var protectedPayload = new SecretPayload();
protectedPayload.Metadata[ProtectedValueKey] = protector.Protect(payload.Value);
foreach (var item in payload.Metadata.Where(x => !string.Equals(x.Key, ProtectedValueKey, StringComparison.OrdinalIgnoreCase)))
protectedPayload.Metadata[item.Key] = item.Value;
return Task.FromResult(protectedPayload);
}
public Task<SecretPayload?> ReadAsync(Secret secret, SecretVersion version, CancellationToken cancellationToken = default)
{
if (!version.Payload.Metadata.TryGetValue(ProtectedValueKey, out var protectedValue))
return Task.FromResult<SecretPayload?>(null);
return Task.FromResult<SecretPayload?>(SecretPayload.FromValue(protector.Unprotect(protectedValue)));
}
View on GitHub (pinned to fe9217bdfa)