apache/pulsar · error · RestException
Invalid value for message TTL
Error message
Invalid value for message TTL
What it means
Thrown while updating namespace policies (setPolicies / setNamespaceMessageTTL path): after validating clusters, the broker checks that message_ttl_in_seconds is not negative. A negative message TTL is meaningless (messages would expire before arrival), so the update is rejected with HTTP 412 PRECONDITION_FAILED.
Source
Thrown at pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java:2418
}
private CompletableFuture<Void> validatePoliciesAsync(NamespaceName ns, Policies policies) {
if (!policies.checkAllowedAndReplicationClusters()) {
String msg = String.format("[%s] All replication clusters should be included in allowed clusters."
+ " Repl clusters: %s, allowed clusters: %s",
ns.toString(), policies.replication_clusters, policies.allowed_clusters);
log.info(msg);
return FutureUtil.failedFuture(new RestException(Status.BAD_REQUEST, msg));
}
pulsar().getBrokerService().setCurrentClusterAllowedWhenCreating(ns, policies);
// Validate cluster names and permissions
return Stream.concat(policies.replication_clusters.stream(), policies.allowed_clusters.stream())
.map(cluster -> validateClusterForTenantAsync(ns.getTenant(), cluster))
.reduce(CompletableFuture.completedFuture(null), (a, b) -> a.thenCompose(ignore -> b))
.thenAccept(__ -> {
if (policies.message_ttl_in_seconds != null && policies.message_ttl_in_seconds < 0) {
throw new RestException(Status.PRECONDITION_FAILED, "Invalid value for message TTL");
}
if (policies.bundles != null && policies.bundles.getNumBundles() > 0) {
if (policies.bundles.getBoundaries() == null || policies.bundles.getBoundaries().size() == 0) {
policies.bundles = getBundles(policies.bundles.getNumBundles());
} else {
policies.bundles = validateBundlesData(policies.bundles);
}
} else {
int defaultNumberOfBundles = config().getDefaultNumberOfNamespaceBundles();
policies.bundles = getBundles(defaultNumberOfBundles);
}
if (policies.persistence != null) {
validatePersistencePolicies(policies.persistence);
}
if (policies.retention_policies != null) {View on GitHub (pinned to 820761864e)
Solutions
- Set messageTtlInSeconds to 0 (no TTL) or a positive number of seconds
- To unset/clear the TTL, pass null (omit the field) rather than -1
- Fix the generating script/config so it clamps negative values to null or 0
- Re-send the policies update with the corrected value
Example fix
// before
Policies p = policies();
p.message_ttl_in_seconds = -1; // 412 Invalid value for message TTL
admin.namespaces().setPolicies("my-tenant/my-ns", p);
// after
p.message_ttl_in_seconds = null; // unset, or 0/positive value
admin.namespaces().setPolicies("my-tenant/my-ns", p); Defensive patterns
Strategy: validation
Validate before calling
if (policies.message_ttl_in_seconds != null && policies.message_ttl_in_seconds < 0) {
policies.message_ttl_in_seconds = null; // or 0
} Type guard
boolean isValidTtl(Integer ttl) { return ttl == null || ttl >= 0; } Try / catch
try {
admin.namespaces().setPolicies(ns, policies);
} catch (PulsarAdminException.PreconditionFailedException e) {
log.error("Invalid negative TTL {}; use null to unset or 0 to disable", policies.message_ttl_in_seconds);
throw e;
} Prevention
- Never use -1 as 'unset' for Pulsar policies; use null/omit
- Clamp TTL values at config load time
- Add unit tests covering negative-value rejection in deployment tooling
When it happens
Trigger: PUT /namespaces/{ns} (policies update) with messageTtlInSeconds < 0; passing -1 intending 'unset' when the correct way to unset is null; automation computing TTL from a formula that produced a negative value; client SDKs serializing an unset field as -1.
Common situations: Using -1 to 'disable' TTL (must use null/omit instead); misconfigured YAML/JSON where a variable was empty and coerced to -1; template rendering bugs producing negative values; older scripts written for systems that accepted -1 as unset.
Related errors
- Topic name is not valid
- Partitioned Topic Name should not contain '-partition-'
- interval must be greater than or equal to 0
- maxProducersPerTopic must be 0 or more
- maxConsumersPerTopic must be 0 or more
AI-assisted analysis of apache/pulsar@820761864e (2026-09-06).
Data as JSON: /api/errors/9ca03c298065ad6d.
Report an issue: GitHub.