cloudreve/cloudreve · error

failed to delete files %v: %w

Error message

failed to delete files %v: %w

What it means

Final sub-step of File.Delete (step 5): the file rows themselves are physically deleted (File.Delete().Where(group) under SkipSoftDelete). This error means the terminal DELETE failed after shares/metadata/directlinks were already removed for this group — a partially progressed cascade.

Source

Thrown at inventory/file.go:601

	)

	for i, group := range fileGroups {
		// 4. Delete shares/metadata/directlinks if needed;
		if _, err := f.client.Share.Delete().Where(share.HasFileWith(group)).Exec(ctx); err != nil {
			return nil, nil, fmt.Errorf("failed to delete shares of files %v: %w", group, err)
		}

		if _, err := f.client.Metadata.Delete().Where(metadata.FileIDIn(chunks[i]...)).Exec(schema.SkipSoftDelete(ctx)); err != nil {
			return nil, nil, fmt.Errorf("failed to delete metadata of files %v: %w", group, err)
		}

		if _, err := f.client.DirectLink.Delete().Where(directlink.FileIDIn(chunks[i]...)).Exec(hardDeleteCtx); err != nil {
			return nil, nil, fmt.Errorf("failed to delete direct links of files %v: %w", group, err)
		}

		// 5. Delete files.
		if _, err := f.client.File.Delete().Where(group).Exec(hardDeleteCtx); err != nil {
			return nil, nil, fmt.Errorf("failed to delete files %v: %w", group, err)
		}
	}

	return toBeRecycled, storageReduced, nil
}

func (f *fileClient) Copy(ctx context.Context, args *CopyParameter) (map[int][]*ent.File, StorageDiff, error) {
	files := args.Files
	dstMap := args.DstMap
	pageSize := capPageSize(f.maxSQlParam, intsets.MaxInt, 10)
	// 1. Copy files and metadata
	copyFileStm := lo.Map(files, func(file *ent.File, index int) *ent.FileCreate {

		stm := f.client.File.Create().
			SetName(file.Name).
			SetOwnerID(dstMap[file.FileChildren][0].OwnerID).
			SetSize(file.Size).
			SetType(file.Type).

View on GitHub (pinned to 20c95ad73f)

Solutions

  1. Unwrap to see the exact FK name and which table still references files
  2. Ensure all descendants are included in the same Delete call (folder deletes must collect the full subtree)
  3. Re-run the operation: already-removed dependent rows make the retry proceed further
  4. Retry on deadlock-class causes

Example fix

// before
inv.Delete(ctx, topLevelFiles, props)

// after
all := collectSubtree(ctx, topLevelFiles) // include every descendant
inv.Delete(ctx, all, props)
Defensive patterns

Strategy: try-catch

Validate before calling

// include the whole subtree so no child references a deleted parent
all := append([]*ent.File{}, files...)
all = append(all, collectDescendants(ctx, files)...)
if len(all) == 0 { return nil }

Try / catch

if _, _, err := inv.Delete(ctx, all, props); err != nil {
    var mysqlErr *mysql.MySQLError
    if errors.As(err, &mysqlErr) && mysqlErr.Number == 1451 {
        return fmt.Errorf("file still referenced (child or join row outside batch): %w", err)
    }
    if isDeadlock(err) { _, _, err = inv.Delete(ctx, all, props) }
    return err
}

Prevention

When it happens

Trigger: FK RESTRICT from a table still referencing files (file_entities join rows, child files with parent FK if a child outside the group references a deleted parent); deadlock; connection loss. Also fires if the ent schema's edge ON DELETE behavior was changed without migration.

Common situations: Deleting a parent folder batch while a child file outside the batch still references it (broken batching); concurrent upload creating rows referencing the file; customized schema FKs.

Related errors


AI-assisted analysis of cloudreve/cloudreve@20c95ad73f (2026-08-16). Data as JSON: /api/errors/904c8802b674e04a. Report an issue: GitHub.