kestra-io/kestra · error · SecretNotFoundException

Cannot find secret for key '{key}'.

Error message

Cannot find secret for key '{key}'.

What it means

The `SecretService.findSecret()` method looks up a secret by its key (case-insensitive, uppercased) in the in-memory `decodedSecrets` map. This map is populated at startup from environment variables prefixed with `SECRET_`. If no matching key exists, a `SecretNotFoundException` is thrown. This is a runtime/infrastructure error, not a code error.

Source

Thrown at core/src/main/java/io/kestra/core/secret/SecretService.java:55

                try {
                    String value = entry.getValue().replaceAll("\\R", "");
                    consumer.accept(Map.entry(entry.getKey(), new String(Base64.getDecoder().decode(value))));
                } catch (Exception e) {
                    log.error("Could not decode secret '{}', make sure it is Base64-encoded: {}", entry.getKey(), e.getMessage());
                }
            })
            .collect(
                Collectors.toMap(
                    entry -> entry.getKey().substring(SECRET_PREFIX.length()).toUpperCase(),
                    Map.Entry::getValue
                )
            );
    }

    public String findSecret(String tenantId, String namespace, String key) throws SecretNotFoundException, IOException {
        String secret = decodedSecrets.get(key.toUpperCase());
        if (secret == null) {
            throw new SecretNotFoundException("Cannot find secret for key '" + key + "'.");
        }
        return secret;
    }

    /**
     * Finds the secret in full mode, as a value plus metadata.
     * The default returns the value with empty metadata. Multi-field secret managers override this to add metadata.
     */
    public SecretObject findSecretObject(String tenantId, String namespace, String key) throws SecretNotFoundException, IOException {
        return new SecretObject(findSecret(tenantId, namespace, key));
    }

    public ArrayListTotal<META> list(Pageable pageable, String tenantId, List<QueryFilter> filters) throws IOException {
        final Predicate<String> queryPredicate = filters.stream()
            .filter(filter -> QueryFilter.Field.QUERY.equals(filter.field()) && filter.value() != null)
            .findFirst()
            .map(filter ->
            {

View on GitHub (pinned to 823fada927)

Solutions

  1. Verify the secret environment variable is set with the exact prefix: `SECRET_MY_KEY` for key `MY_KEY`.
  2. Ensure the value is Base64-encoded (the service decodes it; a raw value will fail silently at decode time and the key will be absent).
  3. Check that the env var is actually present in the running process (`env | grep SECRET_`).
  4. In Docker/k8s, verify the secret is mounted as an env var in the container spec.
  5. Confirm there is no tenant/namespace mismatch if using a custom secret manager.

Example fix

# before: secret missing from env
{{ secret('API_KEY') }}
# fix: set the env var with Base64-encoded value
export SECRET_API_KEY=$(echo -n 'my-secret-value' | base64)
Defensive patterns

Strategy: try-catch

Validate before calling

// Check if secret key exists before calling findSecret
import io.kestra.core.secret.SecretService;

public static String getSecretOrEmpty(SecretService service, String tenantId, String namespace, String key) {
    try {
        return service.findSecret(tenantId, namespace, key);
    } catch (SecretNotFoundException e) {
        return null; // or throw a more descriptive error
    }
}

Try / catch

try {
    String secret = secretService.findSecret(tenantId, namespace, key);
} catch (SecretNotFoundException e) {
    log.error("Secret '{}' not found. Ensure SECRET_{} env var is set and Base64-encoded.", key, key.toUpperCase());
    throw e;
}

Prevention

When it happens

Trigger: Referencing `{{ secret('MY_KEY') }}` in a flow when no `SECRET_MY_KEY` environment variable is set. The secret was deleted from the environment or the variable name has a typo. The secret exists but under a different tenant or namespace (in multi-tenant setups with custom secret managers).

Common situations: Secrets are set in a local `.env` file but not loaded into the running process. In Docker/k8s deployments the secret env var was not mounted into the container. A secret key has a typo or case mismatch (the lookup uppercases the key, but the env var prefix must be `SECRET_`).

Related errors


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