{"record":{"id":"50c664e6370f3354","repo":"apache/pulsar","slug":"cluster-data-is-required","errorCode":null,"errorMessage":"cluster data is required","messagePattern":"cluster data is required","errorType":"http","errorClass":"RestException","httpStatus":400,"severity":"info","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java","lineNumber":174,"sourceCode":"            required = true,\n            content = @Content(\n                mediaType = MediaType.APPLICATION_JSON,\n                examples = @ExampleObject(\n                    value = \"\"\"\n                            {\n                               \"serviceUrl\": \"http://pulsar.example.com:8080\",\n                               \"brokerServiceUrl\": \"pulsar://pulsar.example.com:6651\"\n                            }\n                            \"\"\"\n                )\n            )\n        ) ClusterDataImpl clusterData) {\n        validateBothSuperuserAndClusterOperation(cluster, ClusterOperation.CREATE_CLUSTER)\n                .thenCompose(__ -> validatePoliciesReadOnlyAccessAsync())\n                .thenCompose(__ -> {\n                    NamedEntity.checkName(cluster);\n                    if (clusterData == null) {\n                        throw new RestException(Status.BAD_REQUEST, \"cluster data is required\");\n                    }\n                    try {\n                        clusterData.checkPropertiesIfPresent();\n                    } catch (IllegalArgumentException ex) {\n                        throw new RestException(Status.BAD_REQUEST, ex.getMessage());\n                    }\n                    return clusterResources().getClusterAsync(cluster);\n                }).thenCompose(clusterOpt -> {\n                    if (clusterOpt.isPresent()) {\n                        throw new RestException(Status.CONFLICT, \"Cluster already exists\");\n                    }\n                    return clusterResources().createClusterAsync(cluster, clusterData);\n                }).thenAccept(__ -> {\n                    log.info().attr(\"cluster\", cluster).log(\"Created cluster\");\n                    asyncResponse.resume(Response.ok().build());\n                }).exceptionally(ex -> {\n                    log.error()\n                            .attr(\"cluster\", cluster)","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/ClustersBase.java#L156-L192","documentation":"createCluster validates that a ClusterData payload accompanies the PUT /admin/v2/clusters/{cluster} request. The cluster name comes from the path, but the body must contain the cluster configuration (service URLs, etc.); if the body is missing, null, or deserializes to null, it throws HTTP 400 BAD_REQUEST with 'cluster data is required'.","triggerScenarios":"PUT /admin/v2/clusters/{cluster} with an empty body, no Content-Type/application-json, or a JSON body that deserializes to a null ClusterData object (e.g. literal 'null' body).","commonSituations":"Using curl without -d/--data or with an empty -d '{}'-less body; CLI/SDK calls where the cluster data argument is omitted; scripting against the REST API with wrong Content-Type so the body is dropped; proxy/gateway stripping the request body.","solutions":["Send a complete ClusterData JSON body, e.g. {\"serviceUrl\":\"http://broker:8080\",\"serviceUrlTls\":\"https://broker:8443\",\"brokerServiceUrl\":\"pulsar://broker:6650\",\"brokerServiceUrlTls\":\"pulsar+ssl://broker:6651\"}.","Ensure the request Content-Type is application/json and the body is non-empty; with curl use -H 'Content-Type: application/json' -d '{...}'.","Prefer the Java/CLI client (pulsar-admin clusters create) which constructs ClusterData for you, avoiding hand-rolled REST bodies."],"exampleFix":"// before: empty body -> 400\ncurl -X PUT http://broker:8080/admin/v2/clusters/us-west -H 'Content-Type: application/json'\n// after: supply cluster data\ncurl -X PUT http://broker:8080/admin/v2/clusters/us-west -H 'Content-Type: application/json' \\\n  -d '{\"serviceUrl\":\"http://broker:8080\",\"brokerServiceUrl\":\"pulsar://broker:6650\"}'","handlingStrategy":"validation","validationCode":"// validate the payload before PUT /clusters/{cluster}\nif (clusterData == null || clusterData.getServiceUrl() == null\n        || clusterData.getServiceUrl().isEmpty()) {\n    throw new IllegalArgumentException(\"cluster data with serviceUrl is required\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    admin.clusters().createCluster(clusterName, clusterData);\n} catch (PulsarAdminException.BadRequestException e) {\n    if (\"cluster data is required\".equals(e.getMessage())) {\n        log.error(\"PUT sent without a ClusterData body\");\n    }\n}","preventionTips":["Always send a ClusterData JSON body (serviceUrl at minimum) when creating a cluster via REST.","Set Content-Type: application/json on REST calls; verify with -v in curl that the body is transmitted.","Prefer pulsar-admin clusters create or the Java admin client over raw REST to avoid payload mistakes."],"tags":["pulsar","http-400","validation","rest-api","cluster"],"backgroundTag":"missing-request-body","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"}