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

  1. Verify the default secret exists: kubectl get secret <name> -n <namespace> and contains tls.crt/tls.key
  2. Fix the shenyu.server.netty.sni.k8s default-namespace/default-secret config values
  3. Grant the gateway service account RBAC read access to secrets
  4. 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

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


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)