gofr-dev/gofr · error

operation not permitted

Error message

operation not permitted

What it means

ErrOperationNotPermitted is the public sentinel for operations the S3 filesystem cannot perform like a POSIX filesystem. It is returned by Rename when trying to move a file to a different location and by ChDir, since S3 has no working-directory concept. Match with errors.Is(err, s3.ErrOperationNotPermitted).

Source

Thrown at pkg/gofr/datasource/file/s3/fs_dir.go:21

import (
	"context"
	"errors"
	"fmt"
	"mime"
	"os"
	"path"
	"path/filepath"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/service/s3"
	"github.com/aws/aws-sdk-go-v2/service/s3/types"
	file "gofr.dev/pkg/gofr/datasource/file"
)

var (
	ErrOperationNotPermitted = errors.New("operation not permitted")
)

// getBucketName returns the currentS3Bucket.
func getBucketName(filePath string) string {
	return strings.Split(filePath, string(filepath.Separator))[0]
}

// getLocation returns the absolute path of the S3 bucket.
func getLocation(bucket string) string {
	return path.Join(string(filepath.Separator), bucket)
}

// Mkdir creates a directory and any necessary parent directories in the S3 bucket.
//
// This method creates a pseudo-directory in the S3 bucket by putting objects with the specified path prefixes.
// Since S3 uses a flat storage structure, directories are represented by object keys with trailing slashes.
// The method processes the path segments and ensures that each segment (directory) exists in S3.
func (f *FileSystem) Mkdir(name string, _ os.FileMode) error {

View on GitHub (pinned to 187eb24962)

Solutions

  1. Replace ChDir usage with fully qualified paths (bucket/key) on every call.
  2. For moves, implement copy+delete (CopyObject then DeleteObject) yourself.
  3. Abstract behind an interface that offers move/cd only for real filesystem backends, so S3 backends never receive those calls.

Example fix

// before
fs.Chdir("bucket/subdir") // not permitted
// after
f, _ := fs.Open("bucket/subdir/file.txt") // use full path instead
Defensive patterns

Strategy: try-catch

Validate before calling

// avoid calling these on S3-backed fs:
var _ = interface{ ChDir() error } // guard with capability check at startup
func supportsChDir(fs any) bool { _, ok := fs.(interface{ ChDir() error }); return ok }

Try / catch

err := fsObj.ChDir()
if errors.Is(err, s3datasource.ErrOperationNotPermitted) {
    // use fully qualified bucket/key paths instead
}

Prevention

When it happens

Trigger: 1) Rename(oldname, newname) where the two paths have different parent directories; 2) any call to ChDir on the S3 filesystem.

Common situations: Treating the S3 datasource as a drop-in for os.FileSystem (moving files, changing directories); tools ported from local-disk code paths; attempts to implement directory navigation in a flat key-value store.

Related errors


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