{"record":{"id":"911c3f1684248eff","repo":"apolloconfig/apollo","slug":"namespace-s-is-modified-by-s","errorCode":null,"errorMessage":"namespace:%s is modified by %s","messagePattern":"namespace:(.+?) is modified by (.+?)","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/aop/NamespaceAcquireLockAspect.java","lineNumber":164,"sourceCode":"  }\n\n  private void tryLock(long namespaceId, String user) {\n    NamespaceLock lock = new NamespaceLock();\n    lock.setNamespaceId(namespaceId);\n    lock.setDataChangeCreatedBy(user);\n    lock.setDataChangeLastModifiedBy(user);\n    namespaceLockService.tryLock(lock);\n  }\n\n  private void checkLock(Namespace namespace, NamespaceLock namespaceLock, String currentUser) {\n    if (namespaceLock == null) {\n      throw new ServiceException(\n          String.format(\"Check lock for %s failed, please retry.\", namespace.getNamespaceName()));\n    }\n\n    String lockOwner = namespaceLock.getDataChangeCreatedBy();\n    if (!lockOwner.equals(currentUser)) {\n      throw new BadRequestException(\n          \"namespace:\" + namespace.getNamespaceName() + \" is modified by \" + lockOwner);\n    }\n  }\n\n\n}\n","sourceCodeStart":146,"sourceCodeEnd":171,"githubUrl":"https://github.com/apolloconfig/apollo/blob/d95fc18d112589efc09ddcbe1507047584d55251/apollo-adminservice/src/main/java/com/ctrip/framework/apollo/adminservice/aop/NamespaceAcquireLockAspect.java#L146-L171","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before: two different operators editing same namespace\nitemDTO.setDataChangeLastModifiedBy(\"userB\");\nopenApi.updateItem(appId, env, cluster, namespace, itemId, itemDTO);\n\n// after: use a consistent operator / service account for automated edits\nitemDTO.setDataChangeLastModifiedBy(\"ci-bot\");\nopenApi.updateItem(appId, env, cluster, namespace, itemId, itemDTO);\n\n// to release a stale lock manually (DB-level):\n// DELETE FROM NamespaceLock WHERE namespaceId = <id>;","handlingStrategy":"validation","validationCode":"// Before writing, check who currently holds the lock\nNamespaceLock currentLock = namespaceLockService.findLock(namespace.getId());\nif (currentLock != null && !operator.equals(currentLock.getDataChangeCreatedBy())) {\n    // warn user or queue the edit; do not proceed with the write\n    throw new IllegalStateException(\"Namespace is locked by \" + currentLock.getDataChangeCreatedBy());\n}","typeGuard":null,"tryCatchPattern":"try {\n    openApi.updateItem(appId, env, cluster, namespace, itemId, itemDTO);\n} catch (BadRequestException e) {\n    if (e.getMessage().contains(\"is modified by\")) {\n        // Notify user of concurrent edit conflict; do NOT auto-retry\n        String lockOwner = extractOwnerFromMessage(e.getMessage());\n        notifyConflict(namespaceName, lockOwner);\n    } else {\n        throw e;\n    }\n}","preventionTips":["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."],"tags":["concurrency","locking","namespace-lock","conflict","bad-request","apollo-adminservice"],"backgroundTag":null,"analyzedSha":"d95fc18d112589efc09ddcbe1507047584d55251","analyzedAt":"2026-08-14T04:00:05.477Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}