elsa-workflows/elsa-core · error · InvalidOperationException
A configuration key is required.
Error message
A configuration key is required.
What it means
ConfigurationSecretStore reads secrets from application configuration, so each write payload must carry a metadata entry naming the configuration key the secret maps to. When payload.Metadata lacks that key or it is blank, WriteAsync refuses the operation with an InvalidOperationException. No value is actually written to configuration by this store.
Solutions
- Add the required configuration-key metadata entry to the payload before calling WriteAsync.
- Validate payloads in your UI/API layer before dispatching writes to a configuration-backed store.
- If configuration-backed writes are not desired, use EncryptedSecretStore instead, which stores values rather than configuration references.
Example fix
// before
var payload = new SecretPayload { Value = value };
await store.WriteAsync(secret, version, payload, ct);
// after
var payload = new SecretPayload { Value = value };
payload.Metadata[ConfigurationSecretStore.ConfigurationKeyMetadataName] = "MyApp:ApiKey";
await store.WriteAsync(secret, version, payload, ct); Defensive patterns
Strategy: validation
Validate before calling
if (!payload.Metadata.TryGetValue(ConfigurationSecretStore.ConfigurationKeyMetadataName, out var key) || string.IsNullOrWhiteSpace(key))
throw new ArgumentException("Payload must specify the configuration key metadata before writing to ConfigurationSecretStore."); Type guard
bool HasConfigKey(SecretPayload p) =>
p.Metadata.TryGetValue(ConfigurationSecretStore.ConfigurationKeyMetadataName, out var k) && !string.IsNullOrWhiteSpace(k); Try / catch
try
{
await store.WriteAsync(secret, version, payload, ct);
}
catch (InvalidOperationException ex) when (ex.Message == "A configuration key is required.")
{
logger.LogError("Refusing write: missing configuration key metadata for secret {SecretId}.", secret.Id);
} Prevention
- Always attach the configuration-key metadata when building payloads for this store.
- Validate payload metadata in your service layer before dispatching writes.
- Document the required metadata key for consumers of your API.
When it happens
Trigger: Calling WriteAsync on ConfigurationSecretStore with a SecretPayload whose Metadata dictionary does not contain the ConfigurationKeyMetadataName key, or contains it with null/whitespace value.
Common situations: Building payloads generically from user input without enforcing the metadata key; UI clients that omit advanced metadata; migrating secrets from another store where the metadata key was named differently.
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
- Configuration connection
- The configured secret binding is invalid.
- The OpenID Connect connection configuration is invalid.
- The secret field name must contain a letter or digit.
- The configured secret binding could not be resolved.
AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13).
Data as JSON: /api/errors/e4b939b493a82fdb.
Report an issue: GitHub.
Appendix: source
Thrown at src/modules/Elsa.Secrets/Stores/ConfigurationSecretStore.cs:22
namespace Elsa.Secrets.Stores;
public class ConfigurationSecretStore(IConfiguration configuration, IOptions<SecretsOptions> options) : ISecretStore
{
private const string ConfigurationKeyMetadataName = "configurationKey";
public string Name => SecretStoreNames.Configuration;
public SecretStoreDescriptor Descriptor { get; } = new(
SecretStoreNames.Configuration,
"Configuration",
"Reads values from application configuration without storing the value in Elsa.",
SecretStoreCapabilities.Read | SecretStoreCapabilities.Write | SecretStoreCapabilities.Test,
true);
public Task<SecretPayload> WriteAsync(Secret secret, SecretVersion version, SecretPayload payload, CancellationToken cancellationToken = default)
{
if (!payload.Metadata.TryGetValue(ConfigurationKeyMetadataName, out var key) || string.IsNullOrWhiteSpace(key))
throw new InvalidOperationException("A configuration key is required.");
return Task.FromResult(new SecretPayload { Metadata = new Dictionary<string, string>(payload.Metadata, StringComparer.OrdinalIgnoreCase) });
}
public Task<SecretPayload?> ReadAsync(Secret secret, SecretVersion version, CancellationToken cancellationToken = default)
{
if (!version.Payload.Metadata.TryGetValue(ConfigurationKeyMetadataName, out var key) || string.IsNullOrWhiteSpace(key))
return Task.FromResult<SecretPayload?>(null);
var configuredValue = configuration[$"{options.Value.ConfigurationSectionName}:{key}"] ?? configuration[key];
return configuredValue == null ? Task.FromResult<SecretPayload?>(null) : Task.FromResult<SecretPayload?>(SecretPayload.FromValue(configuredValue));
}
public Task DeleteAsync(Secret secret, CancellationToken cancellationToken = default) => Task.CompletedTask;
public async Task<bool> TestAsync(Secret secret, SecretVersion version, CancellationToken cancellationToken = default)
{
var payload = await ReadAsync(secret, version, cancellationToken);View on GitHub (pinned to fe9217bdfa)