apolloconfig/apollo · warning · BadRequestException

AppId not equal. AppId in path = %s, AppId in payload = %s

Error message

AppId not equal. AppId in path = %s, AppId in payload = %s

What it means

BadRequestException (HTTP 400) from ClusterController.createCluster (POST apps/{appId}/envs/{env}/clusters). It asserts the path appId equals cluster.getAppId() from the ClusterDTO body; the %s/%s placeholders are filled by Guava lenientFormat with appId then cluster.getAppId(). Reaching the service with mismatched ids would create a cluster under the wrong app, so it is blocked up front.

Source

Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/portal/controller/ClusterController.java:61

@RestController
public class ClusterController {

  private final ClusterService clusterService;
  private final UserInfoHolder userInfoHolder;

  public ClusterController(final ClusterService clusterService,
      final UserInfoHolder userInfoHolder) {
    this.clusterService = clusterService;
    this.userInfoHolder = userInfoHolder;
  }

  @PreAuthorize(value = "@unifiedPermissionValidator.hasCreateClusterPermission(#appId)")
  @PostMapping(value = "apps/{appId}/envs/{env}/clusters")
  @ApolloAuditLog(type = OpType.CREATE, name = "Cluster.create")
  public ClusterDTO createCluster(@PathVariable String appId, @PathVariable String env,
      @Valid @RequestBody ClusterDTO cluster) {
    if (!Objects.equals(appId, cluster.getAppId())) {
      throw new BadRequestException("AppId not equal. AppId in path = %s, AppId in payload = %s",
          appId, cluster.getAppId());
    }

    String operator = userInfoHolder.getUser().getUserId();
    cluster.setDataChangeLastModifiedBy(operator);
    cluster.setDataChangeCreatedBy(operator);

    return clusterService.createCluster(Env.valueOf(env), cluster, operator);
  }

  @PreAuthorize(value = "@unifiedPermissionValidator.isSuperAdmin()")
  @DeleteMapping(value = "apps/{appId}/envs/{env}/clusters/{clusterName:.+}")
  @ApolloAuditLog(type = OpType.DELETE, name = "Cluster.delete")
  public ResponseEntity<Void> deleteCluster(@PathVariable String appId, @PathVariable String env,
      @PathVariable String clusterName) {
    clusterService.deleteCluster(Env.valueOf(env), appId, clusterName,
        userInfoHolder.getUser().getUserId());
    return ResponseEntity.ok().build();

View on GitHub (pinned to d95fc18d11)

Solutions

  1. Set cluster.setAppId(appId) using the exact path value before submitting.
  2. If cloning a cluster from another app, copy only clusterName/config and reset appId.
  3. Assert path-vs-body appId equality in the client (see validationCode).
  4. Log both values on failure to catch subtle normalization differences.

Example fix

// before
POST /apps/sample-app/envs/DEV/clusters
body: { "appId": "sampleApp", "name": "cluster-x" }

// after
POST /apps/sample-app/envs/DEV/clusters
body: { "appId": "sample-app", "name": "cluster-x" }
Defensive patterns

Strategy: validation

Validate before calling

// Assert path appId == ClusterDTO.appId before POST apps/{appId}/envs/{env}/clusters.
if (!Objects.equals(pathAppId, clusterDto.getAppId())) {
  clusterDto.setAppId(pathAppId);
}
assert Objects.equals(pathAppId, clusterDto.getAppId());

Type guard

static boolean clusterMatchesPath(ClusterDTO cluster, String pathAppId) {
  return cluster != null && Objects.equals(pathAppId, cluster.getAppId());
}

Prevention

When it happens

Trigger: POST /apps/{appId}/envs/{env}/clusters with a ClusterDTO whose appId field does not equal the {appId} in the URL.

Common situations: Reusing a cluster template body across apps without updating appId; UI pre-filling the body from a different cluster; trailing whitespace or case differences in the appId.

Related errors


AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14). Data as JSON: /api/errors/ba52e7ff284a4aba. Report an issue: GitHub.