apache/shenyu · error · ShenyuException

At least one certificate is required

Error message

At least one certificate is required

What it means

In 'manual' SNI mode the operator must list TLS certificates (cert+key file pairs) in configuration. If SNI is enabled, mod=manual, and the certificates list is empty or absent, the server cannot establish any SNI mapping and startup fails.

Solutions

  1. Add at least one certificate entry under shenyu.server.netty.sni.certificates with valid crt/key file paths
  2. Verify the YAML structure puts certificates under sni.certificates with correct field names
  3. Check that referenced cert/key files exist and are readable (a later step loads them)

Example fix

# before
shenyu:
  server:
    netty:
      sni:
        enabled: true
        mod: manual
# after
shenyu:
  server:
    netty:
      sni:
        enabled: true
        mod: manual
        certificates:
          - crt: /etc/shenyu/tls/server.crt
            key: /etc/shenyu/tls/server.key
Defensive patterns

Strategy: validation

Validate before calling

if (sni.isEnabled() && "manual".equals(sni.getMod())
        && (sni.getCertificates() == null || sni.getCertificates().isEmpty())) {
    throw new IllegalArgumentException("sni.certificates must list at least one crt/key pair in manual mode");
}

Try / catch

try {
    SpringApplication.run(GatewayApplication.class, args);
} catch (Exception e) {
    if (rootCauseOf(e, ShenyuException.class).map(x -> x.getMessage().contains("certificate is required")).orElse(false)) {
        log.error("Add sni.certificates entries or switch sni.mod to k8s");
    } else throw e;
}

Prevention

When it happens

Trigger: shenyu.server.netty.sni.enabled=true with shenyu.server.netty.sni.mod=manual but shenyu.server.netty.sni.certificates is empty, missing, or only contains entries that failed to deserialize into SslCrtAndKeyFile.

Common situations: YAML indentation mistake placing certificates under the wrong key; enabling SNI before provisioning cert files; certificate entries typed with wrong property names so the list binds empty.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of apache/shenyu@567142e072 (2026-09-12). Data as JSON: /api/errors/738c8688833ab6de. Report an issue: GitHub.

Appendix: source

Thrown at shenyu-spring-boot-starter/shenyu-spring-boot-starter-gateway/src/main/java/org/apache/shenyu/springboot/starter/netty/ShenyuNettyWebServerConfiguration.java:105

    @ConditionalOnProperty(value = "shenyu.netty.http.web-server-factory-enabled", havingValue = "true", matchIfMissing = true)
    public NettyReactiveWebServerFactory nettyReactiveWebServerFactory(final ObjectProvider<NettyHttpProperties> properties,
                                                                       final ObjectProvider<ShenyuSniAsyncMapping> shenyuSniAsyncMappingProvider,
                                                                       final ObjectProvider<TcpSslContextSpec> tcpSslContextSpecs) {
        NettyReactiveWebServerFactory webServerFactory = new NettyReactiveWebServerFactory();
        NettyHttpProperties nettyHttpProperties = Optional.ofNullable(properties.getIfAvailable()).orElse(new NettyHttpProperties());
        webServerFactory.addServerCustomizers(new EventLoopNettyCustomizer(nettyHttpProperties, httpServer -> {
            HttpServer server = httpServer;
            // Configure sni certificates
            NettyHttpProperties.SniProperties sniProperties = nettyHttpProperties.getSni();
            if (sniProperties.getEnabled()) {
                ShenyuSniAsyncMapping shenyuSniAsyncMapping = shenyuSniAsyncMappingProvider.getIfAvailable();
                if (Objects.isNull(shenyuSniAsyncMapping)) {
                    throw new ShenyuException("Can not find shenyuSniAsyncMapping bean");
                }
                if ("manual".equals(sniProperties.getMod())) {
                    List<SslCrtAndKeyFile> sslCrtAndKeyFiles = sniProperties.getCertificates();
                    if (CollectionUtils.isEmpty(sslCrtAndKeyFiles)) {
                        throw new ShenyuException("At least one certificate is required");
                    }

                    // Use the first certificate as the default certificate (this default certificate will not actually be used)
                    List<SslCrtAndKeyFile> certificates = sslCrtAndKeyFiles;
                    for (SslCrtAndKeyFile certificate : certificates) {
                        try {
                            shenyuSniAsyncMapping.addSslCertificate(certificate);
                        } catch (IOException e) {
                            LOG.error("add certificate error", e);
                        }
                    }

                    SslCrtAndKeyFile defaultCert = certificates.get(0);
                    TcpSslContextSpec defaultSpec = TcpSslContextSpec.forServer(new File(defaultCert.getKeyCertChainFile()),
                            new File(defaultCert.getKeyFile()));
                    
                    server = server.secure(spec -> spec.sslContext(defaultSpec)
                                .setSniAsyncMappings(shenyuSniAsyncMapping), false);

View on GitHub (pinned to 567142e072)