{"record":{"id":"731b5da82197f179","repo":"apache/pulsar","slug":"target-url-is-required","errorCode":null,"errorMessage":"Target URL is required","messagePattern":"Target URL is required","errorType":"http","errorClass":"RestException","httpStatus":400,"severity":"warning","filePath":"pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/MetadataMigrationBase.java","lineNumber":92,"sourceCode":"    }\n\n    @POST\n    @Path(\"/start\")\n    @Operation(summary = \"Start metadata store migration\")\n    @ApiResponses(value = {\n            @ApiResponse(responseCode = \"204\", description = \"Migration started successfully\"),\n            @ApiResponse(responseCode = \"400\", description = \"Invalid target URL\"),\n            @ApiResponse(responseCode = \"409\", description = \"Migration already in progress\"),\n            @ApiResponse(responseCode = \"500\", description = \"Internal server error\")\n    })\n    public void startMigration(\n            @Parameter(description = \"Target metadata store URL\", required = true)\n            @QueryParam(\"target\")\n            String targetUrl) {\n        validateSuperUserAccess();\n\n        if (targetUrl == null || targetUrl.trim().isEmpty()) {\n            throw new RestException(Response.Status.BAD_REQUEST, \"Target URL is required\");\n        }\n\n        try {\n            // Check if metadata store is wrapped with DualMetadataStore\n            if (!(pulsar().getLocalMetadataStore() instanceof DualMetadataStore dualStore)) {\n                throw new RestException(Response.Status.BAD_REQUEST, \"Metadata store is not configured for migration. \"\n                        + \"Please ensure you're using a supported source metadata store (e.g., ZooKeeper).\");\n            }\n\n            // Reject the request if a migration is already in progress or was completed. The migration\n            // flag is always kept in the source store, so read it from there: after a completed\n            // migration the dual store would route the read to the target store.\n            var existingFlag = dualStore.getSourceStore().get(MigrationState.MIGRATION_FLAG_PATH).get();\n            if (existingFlag.isPresent()) {\n                MigrationState currentState = ObjectMapperFactory.getMapper().reader()\n                        .readValue(existingFlag.get().getValue(), MigrationState.class);\n                switch (currentState.getPhase()) {\n                    case PREPARATION, COPYING -> throw new RestException(Response.Status.CONFLICT,","sourceCodeStart":74,"sourceCodeEnd":110,"githubUrl":"https://github.com/apache/pulsar/blob/820761864ed8e2a7d2e52dd9763ad2ae117c1395/pulsar-broker/src/main/java/org/apache/pulsar/broker/admin/impl/MetadataMigrationBase.java#L74-L110","documentation":"A 400 BAD_REQUEST thrown by the metadata migration admin endpoint (MetadataMigrationBase.startMigration) when the required 'target' query parameter is missing or blank. The migration framework needs the target metadata store URL (e.g. a RocksDB or Etcd/metastore URL) to copy metadata to.","triggerScenarios":"Calling POST/PUT on the metadata-migration admin path without the ?target= query parameter, or with target= (empty) or only whitespace.","commonSituations":"Copy/pasting the endpoint example without the query param; URL encoding issue dropping the value; forgetting the target URL after configuring only the source/dual store.","solutions":["Pass the target metadata store URL, e.g. ?target=rocksdb:/data/target-metadata.","Ensure the parameter name is exactly 'target' and non-empty after trimming.","URL-encode the target URL if it contains special characters like ? or &."],"exampleFix":"// before\ncurl -X POST http://broker:8080/admin/v3/migration\n// after\ncurl -X POST 'http://broker:8080/admin/v3/migration?target=rocksdb%3A%2Fdata%2Ftarget-metadata'","handlingStrategy":"validation","validationCode":"if (!targetUrl || targetUrl.trim().isEmpty()) {\n  throw new IllegalArgumentException(\"?target=<metadata store URL> is required\");\n}","typeGuard":null,"tryCatchPattern":"try { startMigration(target); }\ncatch (PulsarAdminException e) {\n  if (e.getStatusCode() == 400) { /* fix the query parameter and retry */ }\n  else throw e;\n}","preventionTips":["Always pass ?target= with a non-blank store URL","URL-encode the target URL","Read the endpoint's @QueryParam documentation before scripting"],"tags":["rest-api","bad-request","metadata-migration"],"backgroundTag":"missing-required-parameter","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"}