apolloconfig/apollo · error · AccessDeniedException

Access is denied

Error message

Access is denied

What it means

Thrown by InstanceController.checkConfigReadAllowed when a USER_TOKEN identity attempts to read instance/config data for a namespace that is hidden from the current user. The shouldHideConfigToCurrentUser check evaluates the token holder's role against the namespace's visibility rules. Maps to HTTP 403 AccessDeniedException.

Solutions

  1. Grant the token holder's account edit or release permission on the target namespace via the Portal permission management page.
  2. Use a CONSUMER token that has release-namespace permission on the target app/env/cluster/namespace.
  3. Switch to an account that is an app administrator for the target app.
Defensive patterns

Strategy: validation

Validate before calling

// Before instance read with USER_TOKEN, verify the namespace is not hidden
if (authType.equals("USER_TOKEN") && isNamespaceHidden(appId, env, clusterName, namespaceName)) {
    throw new SecurityException("Namespace is hidden from the current user. Grant edit/release permission.");
}

Try / catch

try {
    return client.get("/openapi/v1/envs/" + env + "/apps/" + appId + "/clusters/" + clusterName
        + "/namespaces/" + namespaceName + "/instances");
} catch (AccessDeniedException e) {
    logger.warn("USER_TOKEN denied instance read on namespace. Check namespace visibility settings.");
    throw e;
}

Prevention

When it happens

Trigger: GET instance-by-namespace or instance-by-release endpoints with a USER_TOKEN where the namespace is configured as hidden (e.g. the token holder is not the app's admin and has no edit/release role for that namespace).

Common situations: A namespace has restricted visibility (common for production secrets), and a personal access token whose holder has read-only or no access to that namespace attempts to list its instances. The hide-config feature is designed to prevent unauthorized exposure of sensitive configuration data.

Related errors


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

Appendix: source

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

    }
    checkConfigReadAllowed(appId, env, clusterName, namespaceName);
    return ResponseEntity.ok(instanceService.getInstanceCountByNamespace(appId, Env.valueOf(env),
        clusterName, namespaceName));
  }

  private boolean shouldHideConfigToPortalUser(String appId, String env, String clusterName,
      String namespaceName) {
    return UserIdentityConstants.USER.equals(UserIdentityContextHolder.getAuthType())
        && unifiedPermissionValidator.shouldHideConfigToCurrentUser(appId, env, clusterName,
            namespaceName);
  }

  private void checkConfigReadAllowed(String appId, String env, String clusterName,
      String namespaceName) {
    String authType = UserIdentityContextHolder.getAuthType();
    if (UserIdentityConstants.USER_TOKEN.equals(authType) && unifiedPermissionValidator
        .shouldHideConfigToCurrentUser(appId, env, clusterName, namespaceName)) {
      throw new AccessDeniedException("Access is denied");
    }
    if (UserIdentityConstants.CONSUMER.equals(authType) && !unifiedPermissionValidator
        .hasReleaseNamespacePermission(appId, env, clusterName, namespaceName)) {
      throw new AccessDeniedException("Access is denied");
    }
  }

  private ReleaseDTO findReleaseOrThrow(Env env, long releaseId) {
    ReleaseDTO release = releaseService.findReleaseById(env, releaseId);
    if (release == null) {
      throw NotFoundException.releaseNotFound(releaseId);
    }
    return release;
  }

  private void checkReleaseReadAllowed(String env, ReleaseDTO release) {
    if ((UserIdentityConstants.USER.equals(UserIdentityContextHolder.getAuthType())
        || UserIdentityConstants.USER_TOKEN.equals(UserIdentityContextHolder.getAuthType()))

View on GitHub (pinned to d95fc18d11)