cloudreve/cloudreve · error
failed to unlink entity: %v
Error message
failed to unlink entity: %v
What it means
UnlinkEntity removes the file from the entity's m2m file edge and decrements reference count in one Entity.UpdateOne(...).Exec. Failure means the database rejected that update: the entity row no longer exists, a join-table problem, or connectivity. The storage diff is only returned on success.
Source
Thrown at inventory/file.go:887
versionCount++
if versionCount > max {
// By default, eager-loaded entity is sorted by ID in descending order.
// So we can just unlink the entity and it will be the older version.
newDiff, err := f.UnlinkEntity(ctx, e, file, owner)
if err != nil {
return diff, fmt.Errorf("failed to cap file entities: %v", err)
}
diff.Merge(newDiff)
}
}
return diff, nil
}
func (f *fileClient) UnlinkEntity(ctx context.Context, entity *ent.Entity, file *ent.File, owner *ent.User) (StorageDiff, error) {
if err := f.client.Entity.UpdateOne(entity).RemoveFile(file).AddReferenceCount(-1).Exec(ctx); err != nil {
return nil, fmt.Errorf("failed to unlink entity: %v", err)
}
return map[int]int64{owner.ID: entity.Size * int64(-1)}, nil
}
func (f *fileClient) IsStoragePolicyUsedByEntities(ctx context.Context, policyID int) (bool, error) {
res, err := f.client.Entity.Query().Where(entity.StoragePolicyEntities(policyID)).Limit(1).All(ctx)
if err != nil {
return false, fmt.Errorf("failed to check if storage policy is used by entities: %v", err)
}
if len(res) > 0 {
return true, nil
}
return false, nil
}
View on GitHub (pinned to 20c95ad73f)
Solutions
- Treat ent not-found as already-unlinked (benign) and return an empty diff instead of failing
- Make the unlink idempotent and safe to retry
- Check database connectivity and inspect the wrapped %v for the driver-specific cause
Example fix
// before
if err := f.client.Entity.UpdateOne(entity).RemoveFile(file).AddReferenceCount(-1).Exec(ctx); err != nil {
return nil, fmt.Errorf("failed to unlink entity: %v", err)
}
// after
if err := f.client.Entity.UpdateOne(entity).RemoveFile(file).AddReferenceCount(-1).Exec(ctx); err != nil {
if ent.IsNotFound(err) {
return map[int]int64{}, nil
}
return nil, fmt.Errorf("failed to unlink entity: %v", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := f.client.Entity.Get(ctx, entity.ID); err != nil {
if ent.IsNotFound(err) {
return map[int]int64{}, nil // already gone; nothing to unlink
}
return nil, err
} Try / catch
diff, err := f.UnlinkEntity(ctx, entity, file, owner)
if err != nil {
if ent.IsNotFound(err) {
diff = map[int]int64{} // benign: entity already removed
} else {
return diff, err
}
} Prevention
- Treat not-found during unlink as success (idempotent semantics)
- Avoid concurrent unlink of the same entity from multiple workers
- Log reference count changes for auditability during retention runs
When it happens
Trigger: Two workers unlinking the same entity concurrently; the entity deleted before the unlink executes; reference_count constrained below the decremented value (if a check constraint exists); database unavailable.
Common situations: Retention/cleanup jobs racing with upload completion or failure handlers; multi-worker deployments processing the same files.
Related errors
- failed to remove stale entities: %v
- failed to soft delete file %d: %w
- failed to upgrade file primary entity: %v
- failed to create file: %v
- failed to create default entity: %v
AI-assisted analysis of cloudreve/cloudreve@20c95ad73f (2026-08-16).
Data as JSON: /api/errors/4b38d4ca7e03a669.
Report an issue: GitHub.