apolloconfig/apollo · warning · BadRequestException
namespace:%s is modified by %s
Error message
namespace:%s is modified by %s
What it means
A BadRequestException (HTTP 400) thrown from NamespaceAcquireLockAspect.checkLock() when a namespace lock exists but is owned by a different user. Apollo enforces single-writer-per-namespace-per-release via the NamespaceLock table (created_by = lock holder). When the current operator does not match the lock owner, the write is rejected. This is an intentional concurrency-conflict signal, not a bug.
Source
Thrown at apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/aop/NamespaceAcquireLockAspect.java:164
}
private void tryLock(long namespaceId, String user) {
NamespaceLock lock = new NamespaceLock();
lock.setNamespaceId(namespaceId);
lock.setDataChangeCreatedBy(user);
lock.setDataChangeLastModifiedBy(user);
namespaceLockService.tryLock(lock);
}
private void checkLock(Namespace namespace, NamespaceLock namespaceLock, String currentUser) {
if (namespaceLock == null) {
throw new ServiceException(
String.format("Check lock for %s failed, please retry.", namespace.getNamespaceName()));
}
String lockOwner = namespaceLock.getDataChangeCreatedBy();
if (!lockOwner.equals(currentUser)) {
throw new BadRequestException(
"namespace:" + namespace.getNamespaceName() + " is modified by " + lockOwner);
}
}
}
View on GitHub (pinned to d95fc18d11)
Solutions
- Coordinate with the lock owner (named in the error message) to finish or abort their edit session.
- If the lock is stale (owner is no longer active), delete the NamespaceLock row from the database for that namespaceId to release it.
- Disable namespace.lock.switch in biz config if single-writer enforcement is not needed for your workflow.
- Serialize writes from the same integration so a single operator consistently acquires the lock.
Example fix
// before: two different operators editing same namespace
itemDTO.setDataChangeLastModifiedBy("userB");
openApi.updateItem(appId, env, cluster, namespace, itemId, itemDTO);
// after: use a consistent operator / service account for automated edits
itemDTO.setDataChangeLastModifiedBy("ci-bot");
openApi.updateItem(appId, env, cluster, namespace, itemId, itemDTO);
// to release a stale lock manually (DB-level):
// DELETE FROM NamespaceLock WHERE namespaceId = <id>; Defensive patterns
Strategy: validation
Validate before calling
// Before writing, check who currently holds the lock
NamespaceLock currentLock = namespaceLockService.findLock(namespace.getId());
if (currentLock != null && !operator.equals(currentLock.getDataChangeCreatedBy())) {
// warn user or queue the edit; do not proceed with the write
throw new IllegalStateException("Namespace is locked by " + currentLock.getDataChangeCreatedBy());
} Try / catch
try {
openApi.updateItem(appId, env, cluster, namespace, itemId, itemDTO);
} catch (BadRequestException e) {
if (e.getMessage().contains("is modified by")) {
// Notify user of concurrent edit conflict; do NOT auto-retry
String lockOwner = extractOwnerFromMessage(e.getMessage());
notifyConflict(namespaceName, lockOwner);
} else {
throw e;
}
} Prevention
- Coordinate edit sessions so only one operator writes to a namespace at a time.
- Use a consistent service account for automated edits so the lock owner is predictable.
- Clean up stale NamespaceLock rows when edit sessions are abandoned.
- Disable namespace.lock.switch if single-writer enforcement is not required.
When it happens
Trigger: Any item create/update/delete or change-set operation on a namespace that another user has already locked (namespace.lock.switch enabled). The lock owner is namespaceLock.getDataChangeCreatedBy(); currentUser is derived from dto.getDataChangeLastModifiedBy() or the operator parameter.
Common situations: Two operators editing the same config namespace simultaneously in different browser tabs or via API; a previous edit session crashed without releasing the lock; automated CI/CD pipelines and manual edits colliding on the same namespace.
Related errors
- Check lock for %s failed, please retry.
- The App Id of path variable and request body is different
- can not delete default cluster!
- The maximum number of items (%s) for this namespace has been
- Comment item's key or value should be blank.
AI-assisted analysis of apolloconfig/apollo@d95fc18d11 (2026-08-14).
Data as JSON: /api/errors/911c3f1684248eff.
Report an issue: GitHub.