vxcontrol/pentagi · error

failed to copy resource %q: %w

Error message

failed to copy resource %q: %w

What it means

copyDirResource inserts one new UserResource row per source entry when copying a directory tree. This error identifies WHICH source file failed to copy (its path is quoted) by wrapping the GORM Create error, and aborts the transaction.

Source

Thrown at backend/pkg/server/services/resources.go:1653

			if err := tx.Delete(&dest).Error; err != nil {
				return result, fmt.Errorf("failed to delete destination for overwrite: %w", err)
			}
			result.Deleted = append(result.Deleted, convertResource(dest))
			if dest.Hash != "" {
				result.OrphanHashes = append(result.OrphanHashes, dest.Hash)
			}
		}

		newRec := models.UserResource{
			UserID: uid,
			Hash:   e.Hash,
			Name:   path.Base(newPath),
			Path:   newPath,
			Size:   e.Size,
			IsDir:  e.IsDir,
		}
		if err := tx.Create(&newRec).Error; err != nil {
			return result, fmt.Errorf("failed to copy resource %q: %w", e.Path, err)
		}
		entry := convertResource(newRec)
		if _, wasExisting := existingSet[newPath]; wasExisting {
			result.Updated = append(result.Updated, entry)
		} else {
			result.Added = append(result.Added, entry)
		}
	}

	return result, nil
}

// ---- DELETE /resources/ ----------------------------------------------------

// DeleteResource deletes one or more files or directories (recursively) by virtual path.
// @Summary Delete a resource (file or directory)
// @Tags Resources
// @Produce json

View on GitHub (pinned to ea665308ba)

Solutions

  1. Retry; if unique-violation, the path was created concurrently — re-run with force=true or pick a new destination
  2. Check column sizes for path/name against the longest produced path
  3. Inspect the wrapped driver error to distinguish constraint vs connectivity
  4. Serialize bulk copy operations per user to avoid races

Example fix

// before
copyDir(uid, src, dst, false) // may 500 with 'failed to copy resource "x": duplicate key'
// after
copyDir(uid, src, dst, true) // allow overwrite of concurrently created paths
Defensive patterns

Strategy: retry

Validate before calling

// pre-check all destination paths before bulk copy
newPaths := computeNewPaths(srcTree, srcPath, dstPath)
existing, err := findResourcesByPaths(uid, newPaths)
if err != nil { return err }
if len(existing) > 0 && !force { return errConflict }

Try / catch

if err := copyDir(uid, src, dst, force); err != nil {
    var qerr *quotedPathErr
    if errors.As(err, &qerr) {
        log.Errorf("copy failed for %s: %v", qerr.path, qerr.cause)
    }
    return err
}

Prevention

When it happens

Trigger: tx.Create fails for a specific entry: unique (user_id, path) violation from a concurrent writer that created the same path after the conflict check, column length exceeded by a deep newPath, or DB failure.

Common situations: Racing copy/move operations on the same destination tree; paths exceeding varchar length after prefix replacement; transient DB outage mid-copy.

Related errors


AI-assisted analysis of vxcontrol/pentagi@ea665308ba (2026-09-01). Data as JSON: /api/errors/67e91bf1b91be44e. Report an issue: GitHub.