{"record":{"id":"73865304fe509a7c","repo":"spring-projects/spring-security","slug":"unable-to-locate-acl-to-update","errorCode":null,"errorMessage":"Unable to locate ACL to update","messagePattern":"Unable to locate ACL to update","errorType":"exception","errorClass":"NotFoundException","httpStatus":null,"severity":"error","filePath":"acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java","lineNumber":415,"sourceCode":"\t * passed MutableAcl object. Also will create an acl_sid entry if needed for the Sid\n\t * that owns the MutableAcl.\n\t * @param acl to modify (a row must already exist in acl_object_identity)\n\t * @throws NotFoundException if the ACL could not be found to update.\n\t */\n\tprotected void updateObjectIdentity(MutableAcl acl) {\n\t\tLong parentId = null;\n\t\tif (acl.getParentAcl() != null) {\n\t\t\tAssert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl().getObjectIdentity(),\n\t\t\t\t\t\"Implementation only supports ObjectIdentityImpl\");\n\t\t\tObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity();\n\t\t\tparentId = retrieveObjectIdentityPrimaryKey(oii);\n\t\t}\n\t\tAssert.notNull(acl.getOwner(), \"Owner is required in this implementation\");\n\t\tLong ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);\n\t\tint count = this.jdbcOperations.update(this.updateObjectIdentity, parentId, ownerSid, acl.isEntriesInheriting(),\n\t\t\t\tacl.getId());\n\t\tif (count != 1) {\n\t\t\tthrow new NotFoundException(\"Unable to locate ACL to update\");\n\t\t}\n\t}\n\n\t/**\n\t * Sets the query that will be used to retrieve the identity of a newly created row in\n\t * the <tt>acl_class</tt> table.\n\t * @param classIdentityQuery the query, which should return the identifier. Defaults\n\t * to <tt>call identity()</tt>\n\t */\n\tpublic void setClassIdentityQuery(String classIdentityQuery) {\n\t\tAssert.hasText(classIdentityQuery, \"New classIdentityQuery query is required\");\n\t\tthis.classIdentityQuery = classIdentityQuery;\n\t}\n\n\t/**\n\t * Sets the query that will be used to retrieve the identity of a newly created row in\n\t * the <tt>acl_sid</tt> table.\n\t * @param sidIdentityQuery the query, which should return the identifier. Defaults to","sourceCodeStart":397,"sourceCodeEnd":433,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java#L397-L433","documentation":"updateObjectIdentity issues an UPDATE on acl_object_identity keyed by the ACL's id (acl.getId()). If the update affects zero rows (count != 1), the persisted ACL row doesn't exist or the in-memory ACL's id doesn't correspond to any row, so NotFoundException(\"Unable to locate ACL to update\") is thrown. This is a row-count invariant check on the final persistence step of updateAcl.","triggerScenarios":"updateAcl called with an ACL whose getId() (database PK) is stale, fabricated, or points to a row deleted by another transaction; passing an ACL deserialized from cache/session after the underlying row was removed.","commonSituations":"Caching MutableAcl objects across requests while ACLs get deleted/recreated (new PK each time); manually constructed AclImpl with a guessed id; two nodes racing update/delete on the same ACL; DB cleaned up between serialization and update.","solutions":["Re-read the ACL via readAclById immediately before mutating and updating, rather than reusing long-lived/cached ACL instances.","Ensure acl.getId() is the actual acl_object_identity PK from the database — never fabricate it.","Catch NotFoundException, reload the ACL (or recreate via createAcl), and re-apply changes.","Serialize permission/entry changes rather than MutableAcl instances if you must persist state between requests."],"exampleFix":"// before\nMutableAcl cached = cache.get(oid); // may hold stale id\napplyChanges(cached);\nmutableAclService.updateAcl(cached);\n// after\nMutableAcl fresh = (MutableAcl) aclService.readAclById(oid);\napplyChanges(fresh);\nmutableAclService.updateAcl(fresh);","handlingStrategy":"try-catch","validationCode":"// reload instead of trusting a cached ACL\nMutableAcl fresh = (MutableAcl) aclService.readAclById(oid); // throws if row gone","typeGuard":"null","tryCatchPattern":"try {\n    mutableAclService.updateAcl(cachedAcl);\n} catch (NotFoundException e) {\n    MutableAcl fresh = (MutableAcl) aclService.readAclById(oid);\n    applyAndSave(fresh);\n}","preventionTips":["Do not cache MutableAcl objects across requests; cache ids and reload","Keep ACL rows and cache invalidation in sync on delete","Never fabricate ACL ids when constructing ACL objects"],"tags":["spring-security","acl","stale-data","update"],"backgroundTag":"entity-not-found","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}