apache/pulsar · critical · TlsMaterialUnavailableException
No TLS material configured for server purpose purpose
Error message
No TLS material configured for server purpose purpose
What it means
The TLS factory resolves a material source for a TlsPurpose. If no registered source applies and the purpose role is SERVER (clients fall back to systemDefaultSource), no server-side certificate identity is configured, so TlsMaterialUnavailableException is thrown — the server could not present any TLS identity.
Source
Thrown at pulsar-common/src/main/java/org/apache/pulsar/common/tls/impl/FileBasedTlsFactory.java:420
}
}
/**
* Resolve a requested purpose to the {@link RegisteredSource} that owns its material, applying the
* role's terminal-resolution rule when nothing is configured for the purpose.
*
* @throws TlsMaterialUnavailableException when a server-role purpose has no material configured
*/
private RegisteredSource resolve(TlsPurpose purpose) {
Objects.requireNonNull(purpose, "purpose must not be null");
RegisteredSource source = registry.get(purpose);
if (source != null) {
return source;
}
if (purpose.role() == TlsPurpose.Role.CLIENT) {
return systemDefaultSource();
}
throw new TlsMaterialUnavailableException(
"No TLS material configured for server purpose " + purpose);
}
private RegisteredSource systemDefaultSource() {
RegisteredSource existing = this.systemDefaultSource;
if (existing != null) {
return existing;
}
synchronized (this) {
if (this.systemDefaultSource == null) {
// System default: verify hostnames, OS trust store, no client certificate. A constant
// source (no files, never rotates).
TlsPolicy defaultPolicy = TlsPolicy.builder().build();
this.systemDefaultSource = new RegisteredSource(
TlsPurpose.CLIENT_DEFAULT, defaultPolicy, null);
}
return this.systemDefaultSource;
}View on GitHub (pinned to 820761864e)
Solutions
- Configure server identity: set keyStorePath (with password) or certificateFilePath + keyFilePath in TlsPolicy
- Ensure the policy object wired into the factory is the one holding the server TLS settings
- Use a client-role TlsPurpose if this path should fall back to system defaults
- Register a custom material source for the purpose if provisioning programmatically
Example fix
// before
TlsPolicy policy = TlsPolicy.builder().tlsEnabled(true).build(); // no server identity
// after
TlsPolicy policy = TlsPolicy.builder()
.tlsEnabled(true)
.keyStorePath("/etc/pulsar/broker.keystore.jks")
.keyStorePassword("********")
.build(); Defensive patterns
Strategy: validation
Validate before calling
boolean hasServerIdentity(TlsPolicy p) {
return p.keyStorePath() != null || (p.certificateFilePath() != null && p.keyFilePath() != null);
}
// check before constructing a server-role TlsPurpose Try / catch
try {
factory.createInstance(serverPurpose, SslContext.class).join();
} catch (TlsMaterialUnavailableException e) {
log.error("Server TLS identity not configured: {}", e.getMessage());
} Prevention
- Never enable TLS listeners without keystore or PEM identity configured
- Wire the TlsPolicy object that actually holds server settings into the factory
- Add a startup config check for every server-role purpose
When it happens
Trigger: Building a server-role TLS instance via createInstance/createOneShot while TlsPolicy has neither keyStorePath nor certificateFilePath/keyFilePath set, and no other registered material source matches the purpose.
Common situations: Broker/proxy TLS listeners enabled (tlsEnabled=true) without a server keystore or PEM pair configured; a policy copy that dropped server settings; using a server-role purpose with a client-only policy.
Understand the failure class
- SSL/TLS and certificate errors — how TLS handshakes and certificate validation fail.
Related errors
- TlsPolicy sets certificateFilePath='certificateFilePath' but
- The replication cluster does not provide TLS encrypted servi
- No ${scheme} URL configured for broker ${brokerId}
- Issuer URL does not use https, but must:
- Failed to get TLS certificates from client
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/a7739d8196fdaa5f.
Report an issue: GitHub.