kestra-io/kestra · error · SecretException
Failed to read secret sub-key '%s' from secret '%s'. Ensure
Error message
Failed to read secret sub-key '%s' from secret '%s'. Ensure the secret contains valid JSON value.
What it means
When the secret() function is called with a 'subkey' argument, it attempts to parse the secret value as JSON using Jackson ObjectMapper.readTree(). If the secret is not valid JSON (e.g., a plain string, a number, or malformed JSON), a JsonProcessingException is caught and re-thrown as a SecretException with this message. This means the secret was found but its content cannot be interpreted as a JSON object for sub-key extraction.
Source
Thrown at core/src/main/java/io/kestra/core/runners/pebble/functions/SecretFunction.java:103
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) {
throw new PebbleException(e, e.getMessage(), lineNumber, self.getName());
}
}
@SuppressWarnings("unchecked")
private void consumeSecret(EvaluationContext context, String value) {View on GitHub (pinned to 823fada927)
Solutions
- If the secret is a single value, access it without subkey: {{ secret('API_TOKEN') }}.
- If you need sub-key access, re-store the secret as a JSON object: {"key":"sk-abc123"}.
- Verify the secret content in the secret backend to confirm it is valid JSON.
Example fix
# before — secret value is a plain string, not JSON
{{ secret('API_TOKEN', subkey='key') }}
# after — access the raw value directly
{{ secret('API_TOKEN') }}
# or re-store as JSON: {"key":"sk-abc123"}
{{ secret('API_TOKEN', subkey='key') }} Defensive patterns
Strategy: validation
Validate before calling
# Only use subkey on secrets known to contain valid JSON.
# If unsure, access the raw value first:
# {{ secret('MY_KEY') }}
# If the value is not JSON, re-store it as JSON in the secret backend before using subkey. Prevention
- Store structured secrets as JSON objects from the start.
- Verify secret content format in the secret backend before coding subkey access.
- For single-value secrets (tokens, passwords), never use the subkey argument.
- Add a comment in the flow noting which secrets are JSON and which are plain values.
When it happens
Trigger: Calling {{ secret('API_TOKEN', subkey='key') }} when the secret value is a plain string like 'sk-abc123' (not JSON). The secret was stored as a raw value rather than a JSON object. The secret value contains truncated or corrupted JSON.
Common situations: Secret originally stored as a single value (token, password) but later accessed with a subkey as if it were a JSON object. Secret migration from one backend to another that changed the format. A team member stored a secret without JSON structure.
Related errors
- Cannot find secret sub-key '%s' in secret '%s'.
- Invalid json: {e.getMessage()}
- Unable to parse jq value '{}' with type '{}'
- Unable to transform to json value '{input}' with type '{inpu
- The 'decrypt' function expects two arguments 'key' and 'encr
AI-assisted analysis of kestra-io/kestra@823fada927 (2026-08-14).
Data as JSON: /api/errors/25c4c77ffc4314e2.
Report an issue: GitHub.