apolloconfig/apollo · error · AccessDeniedException
You don't have the permission to modify namespace: %s
Error message
You don't have the permission to modify namespace: %s
What it means
HTTP 403 (AccessDeniedException). Thrown by ItemController.checkSyncPermissions when a bulk item-sync request (syncItems) targets one or more destination namespaces on which the authenticated caller lacks namespace-modify permission. The validator UnifiedPermissionValidator.hasModifyNamespacePermission is evaluated per target namespace; the FIRST namespace that fails is reported back in the message (its OpenNamespaceIdentifier toString). Portal SSO users and OpenAPI consumer tokens are both subject to this check.
Source
Thrown at apollo-portal/src/main/java/com/ctrip/framework/apollo/openapi/v1/controller/ItemController.java:391
return false;
}
return true;
}
private void checkSyncPermissions(OpenNamespaceSyncDTO model) {
OpenNamespaceIdentifier noPermissionNamespace = null;
boolean hasPermission = true;
for (OpenNamespaceIdentifier namespaceIdentifier : model.getSyncToNamespaces()) {
hasPermission = unifiedPermissionValidator.hasModifyNamespacePermission(
namespaceIdentifier.getAppId(), namespaceIdentifier.getEnv(),
namespaceIdentifier.getClusterName(), namespaceIdentifier.getNamespaceName());
if (!hasPermission) {
noPermissionNamespace = namespaceIdentifier;
break;
}
}
if (!hasPermission) {
throw new AccessDeniedException(String
.format("You don't have the permission to modify namespace: %s", noPermissionNamespace));
}
}
private String decodeBase64(String key) {
try {
return decodeBase64(key, Base64.getDecoder());
} catch (IllegalArgumentException standardBase64Exception) {
try {
return decodeBase64(key, Base64.getUrlDecoder());
} catch (IllegalArgumentException urlBase64Exception) {
throw new BadRequestException("Invalid encoded key");
}
}
}
private String decodeBase64(String key, Base64.Decoder decoder) {
return new String(decoder.decode(key), StandardCharsets.UTF_8);View on GitHub (pinned to d95fc18d11)
Solutions
- Inspect the noPermissionNamespace value in the 403 body and grant ModifyNamespace (or EditNamespace) role on that exact appId/env/cluster/namespace to the caller.
- Remove or correct the offending entry from syncToNamespaces in the request payload so every target matches a namespace the caller is authorized to modify.
- If using an OpenAPI consumer token, recreate/extend the token's app scope to include every destination namespace.
- Retry the sync only after re-checking each target with the OpenAPI permission/role endpoints.
Example fix
// before: token only has modify on DEV
syncDTO.getSyncToNamespaces().add(ns(PROD)); // 403 on PROD
// after: drop targets the caller cannot modify, or grant the role first
List<OpenNamespaceIdentifier> targets = syncDTO.getSyncToNamespaces().stream()
.filter(t -> hasModifyPermission(token, t)) // client-side pre-check
.collect(Collectors.toList());
syncDTO.setSyncToNamespaces(targets); Defensive patterns
Strategy: validation
Validate before calling
// Before syncItems, pre-check modify permission on every target namespace.
boolean allOk = true;
OpenNamespaceIdentifier blocked = null;
for (OpenNamespaceIdentifier t : syncDTO.getSyncToNamespaces()) {
// GET /openapi/v1/apps/{appId}/envs/{env}/clusters/{cluster}/namespaces/{ns}/role
// or use a dedicated hasModify permission probe exposed by the portal.
if (!canModify(token, t.getAppId(), t.getEnv(), t.getClusterName(), t.getNamespaceName())) {
allOk = false; blocked = t; break;
}
}
if (!allOk) { /* drop blocked or grant role, do NOT call syncItems */ } Type guard
null
Try / catch
// Distinguish 403 (permission) from other failures.
try {
client.syncItems(appId, env, cluster, ns, syncDTO);
} catch (HttpServerErrorException.Forbidden e) {
// body contains "You don't have the permission to modify namespace: <id>"
String ns = extractNamespaceFrom403(e.getResponseBodyAsString());
log.warn("Missing modify permission on {}, grant role or drop target", ns);
} Prevention
- Grant ModifyNamespace on every destination namespace before configuring cross-env sync.
- Keep the token's app/env scope aligned with all sync targets.
- Sync from the least-privileged source; never assume destination rights from source rights.
When it happens
Trigger: POST /openapi/v1/apps/{appId}/envs/{env}/clusters/{clusterName}/namespaces/{namespaceName}/items/syncItems with an OpenNamespaceSyncDTO whose syncToNamespaces lists a namespace the caller cannot modify. Triggered when the caller has modify rights on the source namespace but not on at least one destination, or when a destination namespace was renamed/deleted and the permission grant is stale.
Common situations: Syncing config across environments (DEV->FAT->PRO) with a token scoped to only one env; a destination cluster/namespace name typo so the permission lookup misses; an admin revoked the ModifyNamespace role after the sync job was configured; cross-app sync where the token lacks the target app's role.
Related errors
- Access is denied
- Create namespace permission is required
- Delete namespace permission is required
- Forbidden operation. Caused by: 1.you don't have release per
- Access is denied
AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14).
Data as JSON: /api/errors/5e62123905e706e9.
Report an issue: GitHub.