{"record":{"id":"329d8db70208507c","repo":"apache/pulsar","slug":"input-bundles-do-not-cover-the-whole-hash-range-f","errorCode":null,"errorMessage":"Input bundles do not cover the whole hash range. first:","messagePattern":"Input bundles do not cover the whole hash range\\. first:","errorType":"http","errorClass":"RestException","httpStatus":400,"severity":"error","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java","lineNumber":2391,"sourceCode":"                                \"Subscription has active connected consumers\");\n                    }\n                    throw new RestException(cause);\n                });\n    }\n\n    protected BundlesData validateBundlesData(BundlesData initialBundles) {\n        SortedSet<String> partitions = new TreeSet<String>();\n        for (String partition : initialBundles.getBoundaries()) {\n            Long partBoundary = Long.decode(partition);\n            partitions.add(String.format(\"0x%08x\", partBoundary));\n        }\n        if (partitions.size() != initialBundles.getBoundaries().size()) {\n                log.debug(\"Input bundles included repeated partition points. Ignored.\");\n                    }\n        try {\n            NamespaceBundleFactory.validateFullRange(partitions);\n        } catch (IllegalArgumentException iae) {\n            throw new RestException(Status.BAD_REQUEST, \"Input bundles do not cover the whole hash range. first:\"\n                    + partitions.first() + \", last:\" + partitions.last());\n        }\n        List<String> bundles = new ArrayList<>();\n        bundles.addAll(partitions);\n        return BundlesData.builder()\n                .boundaries(bundles)\n                .numBundles(bundles.size() - 1)\n                .build();\n    }\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        }","sourceCodeStart":2373,"sourceCodeEnd":2409,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/NamespacesBase.java#L2373-L2409","documentation":"Thrown when setting/updating the bundle boundaries of a namespace (setNamespaceBundleData / updateBundles): NamespaceBundleFactory.validateFullRange requires that the supplied bundle partition points span the entire hash range exactly, ending at the maximum hash. The provided partitions did not cover the full range, so the broker returns HTTP 400 BAD_REQUEST including the first and last partition points for diagnosis.","triggerScenarios":"PUT /namespaces/{ns}/bundles with a boundaries list whose first element is not the minimum hash (0x00000000) or whose last element is not the maximum hash (0xFFFFFFFF); providing a partial list of split points without the endpoints; non-sorted or duplicate boundaries that break full-range coverage after dedup.","commonSituations":"Hand-computed bundle boundaries missing 0x00000000 or 0xFFFFFFFF endpoints; scripts generating N boundaries but forgetting to include the range start/end; boundaries copied from a namespace with a different bundle count; JSON serialization dropping an endpoint.","solutions":["Ensure the boundaries list starts with '0x00000000' and ends with '0xffffffff' (case-insensitive) and is strictly increasing","Validate with validateFullRange-style logic client-side before submitting","If you only want N bundles, use the numBundles parameter (getBundles(numBundles)) instead of hand-crafting boundaries","Deduplicate and sort the partition list before calling the API"],"exampleFix":"// before\nList<String> boundaries = List.of(\"0x00000000\", \"0x40000000\"); // missing end -> 400\nadmin.namespaces().setNamespaceBundleData(\"my-tenant/my-ns\", boundaries);\n// after\nList<String> boundaries = List.of(\"0x00000000\", \"0x40000000\", \"0xffffffff\");\nadmin.namespaces().setNamespaceBundleData(\"my-tenant/my-ns\", boundaries);","handlingStrategy":"validation","validationCode":"List<String> b = boundaries.stream().distinct().sorted().collect(Collectors.toList());\nif (b.isEmpty() || !b.get(0).equalsIgnoreCase(\"0x00000000\")\n        || !b.get(b.size() - 1).equalsIgnoreCase(\"0xffffffff\")) {\n    throw new IllegalArgumentException(\"Boundaries must start at 0x00000000 and end at 0xffffffff\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.namespaces().setNamespaceBundleData(ns, boundaries);\n} catch (PulsarAdminException e) {\n    log.error(\"Invalid bundle boundaries (must cover full hash range): {}\", boundaries);\n    throw e;\n}","preventionTips":["Always include 0x00000000 and 0xffffffff endpoints","Deduplicate and sort boundary lists before submitting","Prefer numBundles-based APIs over manual boundary construction"],"tags":["http-400","bundles","validation","admin-api"],"backgroundTag":"bundle-range-not-covering-full-hash","analyzedSha":"820761864ed8e2a7d2e52dd9763ad2ae117c1395","analyzedAt":"2026-09-06T00:14:20.138Z","contentChangedAt":"2026-09-06T00:14:20.138Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}