Skip to content

fix(rm): allow deletion of DIROBJ objects via --raw (#834)#861

Open
gustcol wants to merge 1 commit intopeak:masterfrom
gustcol:fix/issue-834-rm-dirobj-raw
Open

fix(rm): allow deletion of DIROBJ objects via --raw (#834)#861
gustcol wants to merge 1 commit intopeak:masterfrom
gustcol:fix/issue-834-rm-dirobj-raw

Conversation

@gustcol
Copy link
Copy Markdown

@gustcol gustcol commented Apr 12, 2026

Context

Fixes #834

Root cause

url.IsPrefix() returns true for any remote path ending with /.
validateRMCommand unconditionally checked this and rejected keys
ending with / with the message "s3 bucket/prefix cannot be used
for delete operations"
.

However, S3 allows zero-byte directory marker objects (keys ending
with /). These are real, deletable objects — not S3 prefixes — and
are created by tools like the AWS Console, Cyberduck, or s3cmd.

Fix

When --raw is set the user has explicitly opted out of wildcard and
prefix treatment. The fix extends that opt-out to the IsPrefix() guard
in validateRMCommand:

if srcurl.IsBucket() || (srcurl.IsPrefix() && !srcurl.IsRaw()) {

IsRaw() was already present on URL (it exposed the unexported raw
field). No new public API was added.

Files changed: command/rm.go

Tests

  • Added TestRemoveCommand_DirobjKeyWithRawFlag — passes s3://bucket/dirobj/ with --raw; expects no validation error.
  • Added TestRemoveCommand_PrefixWithoutRawFlag — regression guard confirming the guard still rejects prefix keys without --raw.

Manual verification

# create a dir-marker object with the AWS CLI
aws s3api put-object --bucket my-bucket --key "dirobj/"

# before: fails with "bucket/prefix cannot be used..."
s5cmd rm s3://my-bucket/dirobj/

# after: succeeds
s5cmd rm --raw s3://my-bucket/dirobj/

Fixes peak#834

url.IsPrefix() returns true for any remote path ending with "/", which
caused validateRMCommand to reject those paths with the bucket/prefix
guard. However, S3 allows zero-byte "directory marker" objects whose
keys end with "/" — they are real, deletable objects, not prefixes.

When the caller passes --raw, wildcard expansion is already disabled.
Applying the same opt-out to the prefix guard is semantically correct:
the user is explicitly targeting a specific key, not a prefix glob.

Reproducer:
  s5cmd rm s3://bucket/dirobj/           # ERROR: bucket/prefix cannot be used
  s5cmd rm --raw s3://bucket/dirobj/     # should succeed

After this change:
  The --raw path skips the IsPrefix() guard and reaches the S3 API.
@gustcol gustcol marked this pull request as ready for review April 12, 2026 15:35
@gustcol gustcol requested a review from a team as a code owner April 12, 2026 15:35
@gustcol gustcol requested review from denizsurmeli and igungor and removed request for a team April 12, 2026 15:35
@gustcol
Copy link
Copy Markdown
Author

gustcol commented Apr 12, 2026

Test results

Unit tests (go test -race ./...): PASS

Specific tests:

go test -race -run "TestRemoveCommand_DirobjKeyWithRawFlag|TestRemoveCommand_PrefixWithoutRawFlag" ./command/...
ok  github.com/peak/s5cmd/v2/command

Wasabi smoke test:

# Created a real DIROBJ (key ending with /) via aws s3api put-object
# Then:
$ ./s5cmd rm --raw s3://bucket/dirobj-key/
rm s3://bucket/dirobj-key/
# Exit: 0 — deleted successfully

# Verified deletion:
$ aws s3api head-object --bucket bucket --key "dirobj-key/"
# 404 Not Found — confirmed deleted

Prefix guard without --raw continues to reject trailing-slash keys (no regression).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

s5cmd rm does not delete DIROBJ objects

1 participant