Do not keep retrying against a shared archive until you check whether the object was actually created. Cyberduck can report the transfer as failed because a secondary request was denied even though the file data reached S3. Repeated attempts may leave duplicate versions or unfinished multipart uploads.
The quickest isolation test is to perform a minimal PutObject with the same credentials, endpoint, bucket, and key prefix:
aws s3api put-object \
--bucket YOUR-BUCKET \
--key shared-archive/permission-test.txt \
--body permission-test.txt \
--profile YOUR-PROFILE
If that fails, the returned AWS error is more useful than Cyberduck’s general transfer message. If it succeeds, plain s3:PutObject is allowed and Cyberduck is making another request or adding headers that trigger a different permission check. Compare the command’s key with the exact destination Cyberduck uses, including capitalization and the trailing path. Prefix conditions in bucket policies are easy to get subtly wrong.
@rust_serg58 is right about checking the denied API call, but CloudTrail data events are not necessarily enabled for the bucket. If they were not configured before the test, there may be no matching object-level event to inspect. In that case, turn them on temporarily, reproduce once with a uniquely named file, and record the timestamp, AWS request ID, object key, and principal ARN from the Cyberduck log.
Do not limit the review to the IAM policy attached to the user or role. The effective denial can come from:
- the bucket policy
- an AWS Organizations service control policy
- a permissions boundary
- a role session policy
- a VPC endpoint policy
- a resource policy that allows a different prefix or principal
- a condition on encryption, source network, object tags, or required request headers
An explicit deny in any of those layers wins over an allow. This is why simply adding s3:* to a test statement sometimes changes nothing.
Check the bucket after the failed attempt too. If the object exists, look at the next request in Cyberduck’s log rather than treating the upload itself as denied. If no object appears, test a tiny file and then a larger one to separate a basic write failure from the multipart path. For the shared archive after the access policy is settled, I would use CloudMounter, but I would still run the AWS CLI comparison first because it gives you a clean baseline without client-specific follow-up calls.
Once you have the exact denied action, grant it only on the intended bucket or object prefix. Avoid solving this with broad S3 access, especially for a team archive where an accidental delete or overwrite has a much larger blast radius.