{"record":{"id":"9ca03c298065ad6d","repo":"apache/pulsar","slug":"invalid-value-for-message-ttl","errorCode":null,"errorMessage":"Invalid value for message TTL","messagePattern":"Invalid value for message TTL","errorType":"validation","errorClass":"RestException","httpStatus":412,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java","lineNumber":2418,"sourceCode":"    }\n\n    private CompletableFuture<Void> validatePoliciesAsync(NamespaceName ns, Policies policies) {\n        if (!policies.checkAllowedAndReplicationClusters()) {\n            String msg = String.format(\"[%s] All replication clusters should be included in allowed clusters.\"\n                    + \" Repl clusters: %s, allowed clusters: %s\",\n                    ns.toString(), policies.replication_clusters, policies.allowed_clusters);\n            log.info(msg);\n            return FutureUtil.failedFuture(new RestException(Status.BAD_REQUEST, msg));\n        }\n        pulsar().getBrokerService().setCurrentClusterAllowedWhenCreating(ns, policies);\n\n        // Validate cluster names and permissions\n        return Stream.concat(policies.replication_clusters.stream(), policies.allowed_clusters.stream())\n                    .map(cluster -> validateClusterForTenantAsync(ns.getTenant(), cluster))\n                    .reduce(CompletableFuture.completedFuture(null), (a, b) -> a.thenCompose(ignore -> b))\n            .thenAccept(__ -> {\n                if (policies.message_ttl_in_seconds != null && policies.message_ttl_in_seconds < 0) {\n                    throw new RestException(Status.PRECONDITION_FAILED, \"Invalid value for message TTL\");\n                }\n\n                if (policies.bundles != null && policies.bundles.getNumBundles() > 0) {\n                    if (policies.bundles.getBoundaries() == null || policies.bundles.getBoundaries().size() == 0) {\n                        policies.bundles = getBundles(policies.bundles.getNumBundles());\n                    } else {\n                        policies.bundles = validateBundlesData(policies.bundles);\n                    }\n                } else {\n                    int defaultNumberOfBundles = config().getDefaultNumberOfNamespaceBundles();\n                    policies.bundles = getBundles(defaultNumberOfBundles);\n                }\n\n                if (policies.persistence != null) {\n                    validatePersistencePolicies(policies.persistence);\n                }\n\n                if (policies.retention_policies != null) {","sourceCodeStart":2400,"sourceCodeEnd":2436,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java#L2400-L2436","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nPolicies p = policies();\np.message_ttl_in_seconds = -1; // 412 Invalid value for message TTL\nadmin.namespaces().setPolicies(\"my-tenant/my-ns\", p);\n// after\np.message_ttl_in_seconds = null; // unset, or 0/positive value\nadmin.namespaces().setPolicies(\"my-tenant/my-ns\", p);","handlingStrategy":"validation","validationCode":"if (policies.message_ttl_in_seconds != null && policies.message_ttl_in_seconds < 0) {\n    policies.message_ttl_in_seconds = null; // or 0\n}","typeGuard":"boolean isValidTtl(Integer ttl) { return ttl == null || ttl >= 0; }","tryCatchPattern":"try {\n    admin.namespaces().setPolicies(ns, policies);\n} catch (PulsarAdminException.PreconditionFailedException e) {\n    log.error(\"Invalid negative TTL {}; use null to unset or 0 to disable\", policies.message_ttl_in_seconds);\n    throw e;\n}","preventionTips":["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"],"tags":["http-412","message-ttl","validation","admin-api"],"backgroundTag":"invalid-policy-value","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}