kestra-io/kestra · error · SecretNotFoundException

Cannot find secret sub-key

Error message

Cannot find secret sub-key '%s' in secret '%s'.

What it means

When the secret() function is called with a 'subkey' argument, it loads the secret value, parses it as JSON, and looks for the given sub-key. If the JSON is valid but does not contain the requested key name, a SecretNotFoundException is thrown and wrapped as a PebbleException. This means the secret itself was found and is valid JSON, but the specific field within it is missing.

Solutions

  1. Verify the exact JSON structure of the secret in the secret backend and confirm the sub-key name exists.
  2. Fix typos in the subkey argument (case-sensitive).
  3. If you need the entire secret without a specific field, use full=true instead: {{ secret('KEY', full=true) }}.
  4. Standardize the secret JSON schema across environments.

Example fix

# before — subkey 'host' not in secret JSON {"username":"admin","password":"secret"}
{{ secret('DB_CREDS', subkey='host') }}

# after — use the correct field name, or fetch full object
{{ secret('DB_CREDS', subkey='username') }}
# or inspect the full structure
{{ secret('DB_CREDS', full=true) }}
Defensive patterns

Strategy: validation

Validate before calling

# Before using a subkey, verify it exists in the secret's JSON.
# Use full=true to inspect the structure first during development:
# {{ secret('MY_KEY', full=true) }}
# Then hardcode the confirmed subkey name:
# {{ secret('MY_KEY', subkey='confirmed_field') }}

Prevention

When it happens

Trigger: Calling {{ secret('DB_CREDS', subkey='host') }} when the secret JSON is {"username":"admin","password":"secret"} but has no 'host' field. Typo in the subkey name. The secret was updated and the field was renamed or removed.

Common situations: Secret schema changed (e.g., rotating from flat key names to nested structure). Typo in the subkey argument. Different environments (dev/prod) having different JSON structures for the same secret key.

Related errors


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

Appendix: source

Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/SecretFunction.java:97

                SecretObject secretObject = secretService.get().findSecretObject(flowTenantId, namespace, key);
                consumeSecret(context, secretObject.value());

                Map<String, Object> result = new LinkedHashMap<>();
                result.put(VALUE_KEY, secretObject.value());
                if (!secretObject.metadata().isEmpty()) {
                    secretObject.metadata().values().forEach(value -> consumeSecret(context, value));
                    result.put(METADATA_KEY, secretObject.metadata());
                }
                return result;
            }

            String secret = secretService.get().findSecret(flowTenantId, namespace, key);

            if (subkey != null && !subkey.isEmpty()) {
                try {
                    JsonNode subkeys = OBJECT_MAPPER.readTree(secret);
                    if (!subkeys.has(subkey)) {
                        throw new SecretNotFoundException("Cannot find secret sub-key '" + subkey + "' in secret '" + key + "'.");
                    } else {
                        JsonNode jsonNode = subkeys.get(subkey);
                        secret = jsonNode.isValueNode() ? jsonNode.asText() : jsonNode.toString();
                    }
                } catch (JsonProcessingException e) {
                    throw new SecretException(
                        String.format(
                            "Failed to read secret sub-key '%s' from secret '%s'. Ensure the secret contains valid JSON value.",
                            subkey,
                            key
                        )
                    );
                }
            }

            consumeSecret(context, secret);
            return secret;
        } catch (SecretException | IOException e) {

View on GitHub (pinned to 823fada927)