gofr-dev/gofr · error

%w: renaming as well as moving file to different location is

Error message

%w: renaming as well as moving file to different location is not allowed

What it means

Rename in the S3 filesystem only supports renaming in place: S3 objects cannot be atomically moved between prefixes, so when oldname and newname have different directory components the call is refused with this wrap of the public sentinel ErrOperationNotPermitted. errors.Is(err, s3.ErrOperationNotPermitted) matches.

Source

Thrown at pkg/gofr/datasource/file/s3/fs.go:347

	st := statusErr

	defer f.sendOperationStats(&FileLog{
		Operation: "RENAME",
		Location:  getLocation(f.config.BucketName),
		Status:    &st,
		Message:   &msg,
	}, time.Now())

	// check if they have the same name or not
	if oldname == newname {
		f.logger.Logf("%q & %q are same", oldname, newname)
		return nil
	}

	// check if both exist at same location or not
	if path.Dir(oldname) != path.Dir(newname) {
		f.logger.Errorf("%q & %q are not in same location", oldname, newname)
		return fmt.Errorf("%w: renaming as well as moving file to different location is not allowed", ErrOperationNotPermitted)
	}

	// check if it is a directory
	if path.Ext(oldname) == "" {
		return f.renameDirectory(&st, &msg, oldname, newname)
	}

	// check if they are of the same type or not
	if path.Ext(oldname) != path.Ext(newname) {
		f.logger.Errorf("new file must be same as the old file type")
		return fmt.Errorf("%w: new filename must match the old file's type", errIncorrectFileType)
	}

	_, err := f.conn.CopyObject(context.TODO(), &s3.CopyObjectInput{
		Bucket: aws.String(f.config.BucketName),
		// The source object can be up to 5 GB.
		// If the source object is an object that was uploaded by using a multipart upload, the object copy
		// will be a single part object after the source object is copied to the destination bucket.

View on GitHub (pinned to 187eb24962)

Solutions

  1. Rename only within the same directory/prefix: same path.Dir for both names.
  2. Implement a move manually: CopyObject to the new key followed by DeleteObject on the old key.
  3. If using the gofr fs API, first change directory/bucket context appropriately, or restructure code to upload directly to the target prefix.

Example fix

// before
fs.Rename("bucket/logs/old.txt", "bucket/archive/old.txt") // not permitted
// after
// copy then delete
fs.Copy? -> use conn.CopyObject(newKey) + DeleteObject(oldKey), or rename in place:
fs.Rename("bucket/logs/old.txt", "bucket/logs/renamed.txt")
Defensive patterns

Strategy: validation

Validate before calling

func sameDir(old, new string) bool { return path.Dir(old) == path.Dir(new) }
// guard before calling:
if !sameDir(oldName, newName) { /* do copy+delete instead of Rename */ }

Try / catch

if err := fs.Rename(old, new); err != nil {
    if errors.Is(err, s3datasource.ErrOperationNotPermitted) {
        // fallback: CopyObject(new) + DeleteObject(old)
    }
    return err
}

Prevention

When it happens

Trigger: Calling Rename(oldname, newname) where path.Dir(oldname) != path.Dir(newname), e.g. Rename("bucket/dir1/a.txt", "bucket/dir2/a.txt") — effectively a move across locations.

Common situations: Porting POSIX os.Rename-based code to the S3 filesystem expecting move semantics; reorganizing objects into different prefixes; generic file-moving utilities wired to the S3 backend.

Related errors


AI-assisted analysis of gofr-dev/gofr@187eb24962 (2026-09-01). Data as JSON: /api/errors/dd74df487bc4f047. Report an issue: GitHub.