spring-projects/spring-security · error · NotFoundException
Unable to locate ACL to update
Error message
Unable to locate ACL to update
What it means
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.
Source
Thrown at acl/src/main/java/org/springframework/security/acls/jdbc/JdbcMutableAclService.java:415
* passed MutableAcl object. Also will create an acl_sid entry if needed for the Sid
* that owns the MutableAcl.
* @param acl to modify (a row must already exist in acl_object_identity)
* @throws NotFoundException if the ACL could not be found to update.
*/
protected void updateObjectIdentity(MutableAcl acl) {
Long parentId = null;
if (acl.getParentAcl() != null) {
Assert.isInstanceOf(ObjectIdentityImpl.class, acl.getParentAcl().getObjectIdentity(),
"Implementation only supports ObjectIdentityImpl");
ObjectIdentityImpl oii = (ObjectIdentityImpl) acl.getParentAcl().getObjectIdentity();
parentId = retrieveObjectIdentityPrimaryKey(oii);
}
Assert.notNull(acl.getOwner(), "Owner is required in this implementation");
Long ownerSid = createOrRetrieveSidPrimaryKey(acl.getOwner(), true);
int count = this.jdbcOperations.update(this.updateObjectIdentity, parentId, ownerSid, acl.isEntriesInheriting(),
acl.getId());
if (count != 1) {
throw new NotFoundException("Unable to locate ACL to update");
}
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in
* the <tt>acl_class</tt> table.
* @param classIdentityQuery the query, which should return the identifier. Defaults
* to <tt>call identity()</tt>
*/
public void setClassIdentityQuery(String classIdentityQuery) {
Assert.hasText(classIdentityQuery, "New classIdentityQuery query is required");
this.classIdentityQuery = classIdentityQuery;
}
/**
* Sets the query that will be used to retrieve the identity of a newly created row in
* the <tt>acl_sid</tt> table.
* @param sidIdentityQuery the query, which should return the identifier. Defaults toView on GitHub (pinned to 96852e8860)
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.
Example fix
// before MutableAcl cached = cache.get(oid); // may hold stale id applyChanges(cached); mutableAclService.updateAcl(cached); // after MutableAcl fresh = (MutableAcl) aclService.readAclById(oid); applyChanges(fresh); mutableAclService.updateAcl(fresh);
Defensive patterns
Strategy: try-catch
Validate before calling
// reload instead of trusting a cached ACL MutableAcl fresh = (MutableAcl) aclService.readAclById(oid); // throws if row gone
Type guard
null
Try / catch
try {
mutableAclService.updateAcl(cachedAcl);
} catch (NotFoundException e) {
MutableAcl fresh = (MutableAcl) aclService.readAclById(oid);
applyAndSave(fresh);
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- Object identity not found for ACL: <objectIdentity>
- AclEntryAfterInvocationProvider.noPermission
- Authenticated principal required to operate with ACLs
- Principal does not have required ACL permissions to perform
- Unable to find ACL information for object identity '{oid}'
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/73865304fe509a7c.
Report an issue: GitHub.