apolloconfig/apollo · error · BadRequestException

App not found: {appId}

Error message

App not found: {appId}

What it means

Thrown by AppController.getApp (GET /openapi/v1/apps/{appId}) when hasReadApplicationPermissionForCurrentIdentity(appId) returns false. For USER_TOKEN auth type, this checks unifiedPermissionValidator.hasReadApplicationPermission(appId). If the user-token does not have read permission for the specific app, the controller deliberately returns 'App not found' rather than 'Access denied' to avoid information leakage about app existence. Results in HTTP 400 (note: the code uses BadRequestException, not AccessDeniedException, for permission failure here).

Source

Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/AppController.java:161

   * @return which apps can be operated by open api
   */
  @Override
  public ResponseEntity<List<OpenAppDTO>> findAppsAuthorized() {
    if (UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType())) {
      return ResponseEntity.ok(filterReadableApps(this.appOpenApiService.getAllApps()));
    }
    Set<String> appIds = findAppIdsAuthorizedByCurrentIdentity();
    return ResponseEntity.ok(appOpenApiService.getAppsInfo(new ArrayList<>(appIds)));
  }

  /**
   * get single app info (new added)
   */
  @Override
  public ResponseEntity<OpenAppDTO> getApp(String appId) {
    requireReadApplicationPermissionForUserToken(appId);
    if (!hasReadApplicationPermissionForCurrentIdentity(appId)) {
      throw new BadRequestException("App not found: " + appId);
    }
    List<OpenAppDTO> apps = appOpenApiService.getAppsInfo(Collections.singletonList(appId));
    if (null == apps || apps.isEmpty()) {
      throw new BadRequestException("App not found: " + appId);
    }
    return ResponseEntity.ok(apps.get(0));
  }

  /**
   * update app (new added)
   */
  @Override
  @PreAuthorize(value = "@unifiedPermissionValidator.isAppAdmin(#appId)")
  @ApolloAuditLog(type = OpType.UPDATE, name = "App.update")
  public ResponseEntity<Void> updateApp(String appId, OpenAppDTO dto, String operator) {
    if (!Objects.equals(appId, dto.getAppId())) {
      throw new BadRequestException("The App Id of path variable and request body is different");
    }

View on GitHub (pinned to d95fc18d11)

Solutions

  1. Grant the user (associated with the user-token) read permission for the target app via the Portal.
  2. Use a Consumer token that has been assigned the app role via assignAppRoleToConsumer.
  3. Verify you are requesting the correct appId — if the user has no access, the system intentionally hides the app's existence.
Defensive patterns

Strategy: validation

Validate before calling

// For USER_TOKEN, check read permission before calling getApp
if (UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType())) {
    if (!unifiedPermissionValidator.hasReadApplicationPermission(appId)) {
        // User lacks access — inform caller clearly
        throw new AccessDeniedException("No read permission for app: " + appId);
    }
}

Prevention

When it happens

Trigger: Calling getApp(appId) with a USER_TOKEN that lacks read-application permission for the specified appId. For CONSUMER and USER auth types, hasReadApplicationPermissionForCurrentIdentity always returns true, so this only affects USER_TOKEN callers.

Common situations: A user-token was created for a user who does not have read access to the requested app. The user was removed from the app's authorized user list. The user-token is valid globally but not scoped to this particular application.

Related errors


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