elsa-workflows/elsa-core · critical · InvalidOperationException

Elsa Secrets encryption key is not configured. Configure…

Error message

Elsa Secrets encryption key is not configured. Configure SecretsOptions.EncryptionKey before using the encrypted secrets store.

What it means

GetKey reads SecretsOptions.EncryptionKey and throws InvalidOperationException when it is null or empty. The AES-GCM protector cannot encrypt or decrypt without a key, so the library fails fast instead of silently using an insecure default. Message explicitly names the option to configure.

Solutions

  1. Configure SecretsOptions.EncryptionKey (16/24/32 bytes) at startup, e.g. services.Configure<SecretsOptions>(o => o.EncryptionKey = key).
  2. Bind the key from configuration or environment (e.g. base64-decode ELISA_SECRETS_KEY) into options.
  3. Add the key to the deployment's secret store (Azure Key Vault, Kubernetes secret, user-secrets in dev).
  4. Add a startup validation check so the app fails fast with a clear message when the key is absent.

Example fix

// before
builder.Services.AddSecrets(); // EncryptionKey never set
// after
var key = Convert.FromBase64String(builder.Configuration["Elsa:Secrets:EncryptionKey"]!);
builder.Services.Configure<SecretsOptions>(o => o.EncryptionKey = key); // 16/24/32 bytes
Defensive patterns

Strategy: validation

Validate before calling

var key = configuration["Elsa:Secrets:EncryptionKey"];
if (string.IsNullOrWhiteSpace(key))
    throw new InvalidOperationException("Set Elsa:Secrets:EncryptionKey (base64, 16/24/32 bytes) before using encrypted secrets.");
services.Configure<SecretsOptions>(o => o.EncryptionKey = Convert.FromBase64String(key));

Type guard

bool KeyConfigured(SecretsOptions o) => o.EncryptionKey is { Length: > 0 };

Try / catch

try { var value = protector.Unprotect(stored); }
catch (InvalidOperationException ex) when (ex.Message.Contains("encryption key is not configured"))
{ logger.LogCritical("Secrets encryption key missing; check deployment configuration"); throw; }

Prevention

When it happens

Trigger: Any Protect/Unprotect call (encrypted secret store reads/writes, HmacExternalAuthenticationHandleHasher) when SecretsOptions.EncryptionKey was not set in DI configuration.

Common situations: Forgetting to bind SecretsOptions in Program.cs; key provided via env var not mapped to options; Developer/Production config sections missing; new deployment without the key in secret management.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of elsa-workflows/elsa-core@fe9217bdfa (2026-09-13). Data as JSON: /api/errors/3d369b0f3b078989. Report an issue: GitHub.

Appendix: source

Thrown at src/modules/Elsa.Secrets/Services/DefaultSecretValueProtector.cs:45

        if (parts.Length != 4 || parts[0] != "v1")
            throw new InvalidOperationException("The protected secret payload is not supported.");

        var nonce = Convert.FromBase64String(parts[1]);
        var tag = Convert.FromBase64String(parts[2]);
        var ciphertext = Convert.FromBase64String(parts[3]);
        var plaintext = new byte[ciphertext.Length];

        using var aes = new AesGcm(GetKey(), TagSize);
        aes.Decrypt(nonce, ciphertext, tag, plaintext);

        return System.Text.Encoding.UTF8.GetString(plaintext);
    }

    private byte[] GetKey()
    {
        var key = options.Value.EncryptionKey;
        if (key == null || key.Length == 0)
            throw new InvalidOperationException("Elsa Secrets encryption key is not configured. Configure SecretsOptions.EncryptionKey before using the encrypted secrets store.");

        if (key.Length is not (16 or 24 or 32))
            throw new InvalidOperationException("Elsa Secrets encryption key must be exactly 16, 24, or 32 bytes.");

        return key;
    }
}

View on GitHub (pinned to fe9217bdfa)