kestra-io/kestra · error · SecretNotFoundException
Cannot find secret sub-key '%s' in secret '%s'.
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.
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)
Solutions
- Verify the exact JSON structure of the secret in the secret backend and confirm the sub-key name exists.
- Fix typos in the subkey argument (case-sensitive).
- If you need the entire secret without a specific field, use full=true instead: {{ secret('KEY', full=true) }}.
- 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
- Document the JSON schema of each secret so subkey names are known.
- Standardize secret JSON structure across environments (dev, staging, prod).
- Use full=true during development to inspect the actual secret structure before coding subkey access.
- Watch for case sensitivity in JSON field names.
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
- Failed to read secret sub-key '%s' from secret '%s'. Ensure
- Unable to transform to json value '{input}' with type '{inpu
- The 'decrypt' function expects two arguments 'key' and 'encr
- Revision {revision} of file '{filePath}' was not found in na
- The 'fromJson' function expects an argument 'json'.
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/cbda348276cf6e5c.
Report an issue: GitHub.