apache/shenyu · error · ShenyuException
Can not read cert and key from default secret
Error message
Can not read cert and key from default secret %s/%s
What it means
In k8s SNI mode the default TLS secret (tls.crt/tls.key) is read from the cluster. Note the inverted condition in the source: this message is thrown when secretData is NOT empty's opposite branch — actually when the secret's data map is empty/non-empty mis-handled, the code reads the map when it's empty or throws when data exists but the lookup fails, surfacing as failure to read cert and key from the named default secret.
Solutions
- Verify the default secret exists: kubectl get secret <name> -n <namespace> and contains tls.crt/tls.key
- Fix the shenyu.server.netty.sni.k8s default-namespace/default-secret config values
- Grant the gateway service account RBAC read access to secrets
- Use a valid TLS secret created via `kubectl create secret tls`
Example fix
# before kubectl create secret generic my-tls --from-file=cert=server.crt # after kubectl create secret tls my-tls --cert=server.crt --key=server.key
Defensive patterns
Strategy: try-catch
Validate before calling
Secret secret = client.secrets().inNamespace(ns).withName(name).get();
if (secret == null || secret.getData() == null || !secret.getData().containsKey("tls.crt")
|| !secret.getData().containsKey("tls.key")) {
throw new IllegalStateException("Secret " + ns + "/" + name + " missing tls.crt/tls.key");
} Try / catch
try {
factory = nettyReactiveWebServerFactory(...);
} catch (Exception e) {
if (rootCauseOf(e, ShenyuException.class).map(x -> x.getMessage().contains("Can not read cert and key")).orElse(false)) {
log.error("Verify default secret {}/{} exists and contains tls.crt/tls.key", ns, name);
} else throw e;
} Prevention
- Create TLS secrets with `kubectl create secret tls` so keys are exactly tls.crt/tls.key
- Verify RBAC allows the gateway service account to read secrets in the namespace
- Confirm namespace/name values match the cluster before deploy
When it happens
Trigger: tcpSslContextSpec is asked for the default secret (defaultNamespace/defaultName) but the Kubernetes Secret cannot be read or its data map does not contain usable tls.crt/tls.key entries, so the catch-all ShenyuException fires.
Common situations: Secret not found due to wrong namespace/name config; service account lacking RBAC permission to read secrets; secret exists but stores keys under different names than tls.crt/tls.key.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- The sslCrtAndKeys can not be null
- Can not find shenyuSniAsyncMapping bean
- At least one certificate is required
- Cannot read the sni mod
- Decrement when domain ssl counts <= 0, an unknown exception…
AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12).
Data as JSON: /api/errors/da83091ba88aa925.
Report an issue: GitHub.
Appendix: source
Thrown at shenyu-spring-boot-starter/shenyu-spring-boot-starter-k8s/src/main/java/org/apache/shenyu/springboot/starter/k8s/IngressControllerConfiguration.java:272
*/
@Bean
@ConditionalOnProperty(value = {"shenyu.netty.http.web-server-factory-enabled", "shenyu.netty.http.sni.enabled"}, havingValue = "true")
public TcpSslContextSpec tcpSslContextSpec(final ObjectProvider<NettyHttpProperties> properties, final ApiClient apiClient) throws ApiException {
NettyHttpProperties nettyHttpProperties = Optional.ofNullable(properties.getIfAvailable()).orElse(new NettyHttpProperties());
NettyHttpProperties.SniProperties sniProperties = nettyHttpProperties.getSni();
if (Objects.nonNull(sniProperties) && sniProperties.getEnabled() && "k8s".equals(sniProperties.getMod())) {
String defaultName = Optional.ofNullable(sniProperties.getDefaultK8sSecretName()).orElse("default-ingress-crt");
String defaultNamespace = Optional.ofNullable(sniProperties.getDefaultK8sSecretNamespace()).orElse("default");
CoreV1Api coreV1Api = new CoreV1Api(apiClient);
V1Secret secret = coreV1Api.readNamespacedSecret(defaultName, defaultNamespace, "true");
Map<String, byte[]> secretData = secret.getData();
if (MapUtils.isEmpty(secretData)) {
InputStream crtStream = new ByteArrayInputStream(secretData.get("tls.crt"));
InputStream keyStream = new ByteArrayInputStream(secretData.get("tls.key"));
return TcpSslContextSpec.forServer(crtStream, keyStream);
} else {
throw new ShenyuException(String.format("Can not read cert and key from default secret %s/%s", defaultNamespace, defaultName));
}
}
return TcpSslContextSpec.forServer(new ByteArrayInputStream(new byte[]{}), new ByteArrayInputStream(new byte[]{}));
}
}
View on GitHub (pinned to 567142e072)