kestra-io/kestra · error · PebbleException

GeneralSecurityException: e.getMessage()

Error message

GeneralSecurityException: e.getMessage()

What it means

Thrown by the Pebble 'encrypt' template function when EncryptionService.encrypt(key, plaintext) raises a GeneralSecurityException. Unlike decrypt(), a clean encrypt rarely fails on data, so this almost always indicates an invalid key: wrong length, non-ASCII/unexpected bytes, or a key that the AES key-derivation/initialization step rejects.

Source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/EncryptFunction.java:40

    public Map<String, String> getArgumentDefaults() {
        return Map.of(
            "key", SecretFunction.NAME + "('encryption_key')",
            "plaintext", "'value_to_encrypt'"
        );
    }

    @Override
    public Object execute(Map<String, Object> args, PebbleTemplate self, EvaluationContext context, int lineNumber) {
        if (!args.containsKey("key") || !args.containsKey("plaintext")) {
            throw new PebbleException(null, "The 'encrypt' function expects two arguments 'key' and 'plaintext'.", lineNumber, self.getName());
        }

        String key = (String) args.get("key");
        String plaintext = (String) args.get("plaintext");
        try {
            return EncryptionService.encrypt(key, plaintext);
        } catch (GeneralSecurityException e) {
            throw new PebbleException(e, e.getMessage(), lineNumber, self.getName());
        }
    }
}

View on GitHub (pinned to 823fada927)

Solutions

  1. Use a key that is exactly 16, 24, or 32 characters/bytes long to satisfy AES key constraints.
  2. Confirm secret('encryption_key') resolves to a non-empty value by printing it in a throwaway debug task or via a dedicated check.
  3. Generate the key once and store it as a Kestra secret, then reference it consistently via the same secret() expression.
  4. Round-trip encrypt() then decrypt() in a test task to confirm the key is accepted.

Example fix

# before - arbitrary short password fails AES key init
enc: "{{ encrypt('pw', value) }}"
# after - proper-length key from a secret
enc: "{{ encrypt(secret('ENCRYPTION_KEY'), value) }}"
Defensive patterns

Strategy: validation

Validate before calling

# Ensure the key resolves to a non-empty string of a valid AES length (16/24/32) before encrypting.
# {{ (secret('ENCRYPTION_KEY') != null and secret('ENCRYPTION_KEY').length in [16,24,32]) ? encrypt(secret('ENCRYPTION_KEY'), value) : null }}

Try / catch

# In a wrapper task (Java):
# try { return EncryptionService.encrypt(key, plaintext); }
# catch (GeneralSecurityException e) { /* surface key-length/validity error */ }

Prevention

When it happens

Trigger: Passing a key whose length is not a valid AES key size; passing a key that contains characters that break key derivation; passing a null resolved by a missing secret; providing a key that is an empty string after secret resolution.

Common situations: Using a short or arbitrary password as the AES key instead of a proper 16/24/32-char key; a secret() expression resolving to empty because the secret does not exist or is in another namespace; passing a UUID or sentence as the key.

Related errors


AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14). Data as JSON: /api/errors/a62a72dbefebb9d3. Report an issue: GitHub.