cloudreve/cloudreve · error

failed to copy file: %w

Error message

failed to copy file: %w

What it means

Thrown by File.Copy when saving one of the new file rows fails (stm.Save(ctx)). Copy inserts clones of the source files into destination folders; the most common concrete cause is a unique-constraint violation on (parent, owner, name) when a file with the same name already exists at the destination.

Source

Thrown at inventory/file.go:643

		if file.PrimaryEntity > 0 {
			stm.SetPrimaryEntity(file.PrimaryEntity)
		}

		if file.Props != nil && dstMap[file.FileChildren][0].OwnerID == file.OwnerID {
			stm.SetProps(file.Props)
		}

		return stm
	})

	metadataStm := []*ent.MetadataCreate{}
	entityStm := []*ent.EntityUpdate{}
	newDstMap := make(map[int][]*ent.File, len(files))
	sizeDiff := int64(0)
	for index, stm := range copyFileStm {
		newFile, err := stm.Save(ctx)
		if err != nil {
			return nil, nil, fmt.Errorf("failed to copy file: %w", err)
		}

		fileMetadata, err := files[index].Edges.MetadataOrErr()
		if err != nil {
			return nil, nil, fmt.Errorf("failed to get metadata of file: %w", err)
		}

		metadataStm = append(metadataStm, lo.FilterMap(fileMetadata, func(metadata *ent.Metadata, index int) (*ent.MetadataCreate, bool) {
			if lo.Contains(args.ExcludedMetadataKeys, metadata.Name) {
				return nil, false
			}
			return f.client.Metadata.Create().
				SetName(metadata.Name).
				SetValue(metadata.Value).
				SetFile(newFile).
				SetIsPublic(metadata.IsPublic), true
		})...)

View on GitHub (pinned to 20c95ad73f)

Solutions

  1. Check for name collisions in the destination before copying, or implement rename-on-conflict (append suffix)
  2. Unwrap to confirm whether it is a duplicate-key error vs driver failure
  3. Pre-download/validate source rows against the current schema before copying
  4. Retry only for transient driver causes, not for unique violations

Example fix

// before
newFiles, diff, err := inv.Copy(ctx, &inventory.CopyParameter{Files: files, DstMap: dst})

// after
// resolve name conflicts first
for _, f := range files {
    if exists, _ := inv.IsNameAvailable(ctx, dstOwner, dstParent, f.Name); !exists {
        f.Name = uniquify(f.Name)
    }
}
newFiles, diff, err := inv.Copy(ctx, &inventory.CopyParameter{Files: files, DstMap: dst})
Defensive patterns

Strategy: validation

Validate before calling

// pre-check destination name availability
for _, f := range files {
    if taken, _ := inv.IsChildExist(ctx, dstParent, dstOwner, f.Name); taken {
        return fmt.Errorf("destination already has %q; rename first", f.Name)
    }
}

Try / catch

if _, _, err := inv.Copy(ctx, params); err != nil {
    var mysqlErr *mysql.MySQLError
    if errors.As(err, &mysqlErr) && mysqlErr.Number == 1062 {
        return fmt.Errorf("name conflict at destination: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Copying into a folder that already contains a file with the identical name; connection/driver failure; a NOT NULL field left unset because source rows predate a schema change; context cancelled mid-loop.

Common situations: Web UI copy without rename policy collides with existing destination file; two concurrent copies of the same source into the same folder race the uniqueness check; copying legacy rows missing fields required by newer ent codegen.

Related errors


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