apolloconfig/apollo · error · AccessDeniedException
Forbidden operation. Caused by: 1.you don't have release per
Error message
Forbidden operation. Caused by: 1.you don't have release permission or 2. you don't have modification permission or 3. you have modification permission but branch has been released
What it means
HTTP 403 (AccessDeniedException). Thrown by NamespaceBranchController.deleteBranch when canDeleteBranch returns false. Deletion is permitted only if the caller has ReleaseNamespace permission, OR has ModifyNamespace permission AND the branch has never been released (loadLatestRelease returns null). All three failure causes map to this single message: (1) no release permission, (2) no modify permission, (3) has modify permission but a release already exists on the branch.
Source
Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/NamespaceBranchController.java:95
@PreAuthorize(
value = "@openapiNamespaceBranchController.canCreateBranch(#appId, #env, #clusterName, #namespaceName)")
@ApolloAuditLog(type = OpType.CREATE, name = "NamespaceBranch.create")
@Override
public ResponseEntity<OpenNamespaceDTO> createBranch(String appId, String env, String clusterName,
String namespaceName, String operator) {
String resolvedOperator = resolveOperator(operator, null);
NamespaceDTO namespaceDTO = namespaceBranchService.createBranch(appId, Env.valueOf(env),
clusterName, namespaceName, resolvedOperator);
return namespaceDTO == null ? ResponseEntity.ok().build()
: ResponseEntity.ok(OpenApiModelConverters.fromNamespaceDTO(namespaceDTO));
}
@Override
@ApolloAuditLog(type = OpType.DELETE, name = "NamespaceBranch.delete")
public ResponseEntity<Void> deleteBranch(String env, String appId, String clusterName,
String namespaceName, String branchName, String operator) {
if (!canDeleteBranch(appId, env, clusterName, namespaceName, branchName)) {
throw new AccessDeniedException(
"Forbidden operation. Caused by: 1.you don't have release permission "
+ "or 2. you don't have modification permission "
+ "or 3. you have modification permission but branch has been released");
}
String resolvedOperator = resolveOperator(operator, null);
namespaceBranchService.deleteBranch(appId, Env.valueOf(env), clusterName, namespaceName,
branchName, resolvedOperator);
return ResponseEntity.ok().build();
}
@Override
public ResponseEntity<OpenNamespaceDTO> findBranch(String appId, String env, String clusterName,
String namespaceName, Boolean extendInfo) {
requireConfigReadForUserToken(appId, env, clusterName, namespaceName);
NamespaceBO namespaceBO =
namespaceBranchService.findBranch(appId, Env.valueOf(env), clusterName, namespaceName);
if (namespaceBO == null) {
return ResponseEntity.ok().build();View on GitHub (pinned to d95fc18d11)
Solutions
- If the branch has been released, do not delete it (Apollo protects released branches); instead abandon the branch or roll back releases first via the release API.
- Grant the caller ReleaseNamespace permission on the namespace so canDeleteBranch succeeds via the release-permission path.
- If deletion of an unreleased branch was intended, grant ModifyNamespace permission to the caller.
- Re-check branch release state with the find-branch / latest-release endpoint before issuing DELETE.
Example fix
// before: caller only has ModifyNamespace, branch already released
client.deleteBranch(appId, env, cluster, ns, branch); // 403
// after: verify release state, then either grant ReleaseNamespace or skip
ReleaseDTO latest = client.loadLatestRelease(appId, env, branch, ns);
if (latest != null) {
// released branch cannot be deleted; grant ReleaseNamespace or abandon
} else {
client.deleteBranch(appId, env, cluster, ns, branch);
} Defensive patterns
Strategy: validation
Validate before calling
// Before DELETE branch: check release state and permission paths.
boolean hasRelease = hasPermission(token, appId, env, cluster, ns, "ReleaseNamespace");
boolean hasModify = hasPermission(token, appId, env, cluster, ns, "ModifyNamespace");
ReleaseDTO latest = client.loadLatestRelease(appId, env, branch, ns); // null if none
boolean deletable = hasRelease || (hasModify && latest == null);
if (!deletable) { /* grant permission or skip delete; do NOT call deleteBranch */ } Type guard
null
Try / catch
try {
client.deleteBranch(appId, env, cluster, ns, branch, operator);
} catch (HttpServerErrorException.Forbidden e) {
// message lists 3 causes; decide grant-vs-skip based on release state
if (branchReleased) log.warn("Released branch cannot be deleted");
else admin.grantReleaseNamespace(appId, ns, operator);
} Prevention
- Never attempt to delete a branch that has been released; Apollo protects it.
- Grant ReleaseNamespace to any automation that manages branch lifecycle.
- Always fetch latest-release state before issuing deleteBranch.
When it happens
Trigger: DELETE /openapi/v1/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName} by a caller holding only ModifyNamespace (not Release) rights on a branch that already has at least one published release; or by a caller holding neither release nor modify permission at all.
Common situations: Trying to delete a gray-release branch that has already been used for an active canary release (Apollo forbids deleting released branches to preserve audit history); a token scoped only to 'Modify config' attempting branch lifecycle operations; cleanup automation running under a token whose roles were narrowed.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Access is denied
- You don't have the permission to modify namespace: %s
- Access is denied
- Create namespace permission is required
- Delete namespace permission is required
AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14).
Data as JSON: /api/errors/7c406b2ce7da75b2.
Report an issue: GitHub.