The error “AttributeError: ‘S3 ServiceResource’ object has no attribute ‘upload_file’” means you’re calling the upload method on the wrong S3 object.
If you work with Amazon S3 in Python, this message pops up when code calls upload_file on the high-level S3 service resource instead of a supported target. In boto3, managed uploads live on the S3 client, on a Bucket resource, or on an Object resource. The fix is to switch to one of those and keep the method name lowercase with an underscore.
AttributeError: ‘S3 ServiceResource’ Object Has No Attribute ‘Upload_File’ — Why It Happens
The S3 service resource returned by boto3.resource('s3') is a top-level handle. It offers factories like Bucket(...) and Object(...), plus collection helpers. It does not expose upload_file directly. That method exists on boto3.client('s3'), on s3.Bucket(...), and on s3.Object(...). Calling it on the service resource triggers the attribute error. A second trigger is a casing slip: writing Upload_File or camelCase instead of upload_file.
Can’t Use Upload_File On S3 Resource — Correct Methods
Pick one of these supported patterns. Each works with the same buckets and keys; choose the style that fits your code.
Use The S3 Client
Switch to the client when you want a direct call with clear parameters. It is concise and pairs well with retry configs.
import boto3
s3 = boto3.client('s3')
s3.upload_file(Filename='/tmp/report.csv',
Bucket='my-bucket',
Key='reports/2025/report.csv')
Use A Bucket Resource
Stay with the resource API if you prefer object-oriented style. Grab a bucket, then call upload_file on it.
import boto3
s3 = boto3.resource('s3')
bucket = s3.Bucket('my-bucket')
bucket.upload_file(Filename='/tmp/report.csv', Key='reports/2025/report.csv')
Use An Object Resource
Target one key by constructing an object and uploading to it. This reads cleanly when the key is reused.
import boto3
s3 = boto3.resource('s3')
obj = s3.Object('my-bucket', 'reports/2025/report.csv')
obj.upload_file(Filename='/tmp/report.csv')
Upload From Memory With upload_fileobj
Skip disk writes when data sits in RAM or streams in. Wrap bytes in a file-like object and send it.
import io, boto3
data = b'col1,col2\n1,2\n'
s3 = boto3.client('s3')
s3.upload_fileobj(io.BytesIO(data), 'my-bucket', 'reports/2025/inline.csv')
Fixes For Common Pitfalls
- Call the method on a client, Bucket, or Object — Replace
s3 = boto3.resource('s3'); s3.upload_file(...)with one of the supported forms above. - Use the exact method name — The name is
upload_file. A capitalizedUpload_Fileor camelCase variant will break. - Keep bucket and key straight —
Bucketis the name of the container;Keyis the full path within it, such asfolder/file.txt. - Pass file paths that exist — A missing local path throws a different error; verify the file before uploading.
- Mind IAM permissions — The upload call needs
s3:PutObjecton the target path and, if setting headers, matching rights. - Upgrade old SDK builds — Very old boto3 releases lacked some helpers like
upload_fileobj. Use a current version to avoid gaps. - Avoid mixing session styles — If you use
boto3.Session(...), create the client or resource from that session to keep credentials aligned.
Quick Reference: Which Method Should You Use?
| Method | Use When | Notes |
|---|---|---|
client.upload_file |
Simple path-based uploads | Easy to pair with transfer config and retries |
Bucket.upload_file |
Code already holds a bucket | Object-oriented style with the resource API |
Object.upload_file |
Same key used across calls | Good when setting extra metadata on the object |
Safe Defaults For Reliable Uploads
Managed transfers split larger files into parts and stream them in parallel. You can tune sizes and concurrency without touching the rest of your code.
Add A TransferConfig
Set part sizes and threads to balance speed and memory on your host.
import boto3
from boto3.s3.transfer import TransferConfig
config = TransferConfig(multipart_threshold=8 * 1024 * 1024,
multipart_chunksize=8 * 1024 * 1024,
max_concurrency=10,
use_threads=True)
s3 = boto3.client('s3')
s3.upload_file('/tmp/big.bin', 'my-bucket', 'data/big.bin', Config=config)
Set Metadata And Content Type
Attach headers on write so files render or download correctly from S3.
s3.upload_file('/tmp/photo.jpg', 'my-bucket', 'media/photo.jpg',
ExtraArgs={
'ContentType': 'image/jpeg',
'Metadata': {'app': 'uploader'}
})
Retry Behavior
Use botocore retries for transient network hiccups. Configure a client with a standard retry profile and keep your code simple.
import boto3
from botocore.config import Config
cfg = Config(retries={'max_attempts': 10, 'mode': 'standard'})
s3 = boto3.client('s3', config=cfg)
Working With Keys, Prefixes, And Paths
A key is not a folder plus a file; it is a single string. S3 shows slashes in the console to mimic folders, but the service stores a flat map of keys. Treat the key as a path-like label that you control.
- Pass a clean key — Skip leading slashes. Use
'reports/2025/report.csv', not'/reports/2025/report.csv'. - Normalize local paths — Convert Windows backslashes to forward slashes in keys. Local path style does not carry over to S3.
- Create prefixes by naming — You do not create folders. Uploading to a key that contains slashes makes the console show folders.
When To Use put_object Instead
put_object writes bytes directly to a key. It suits tiny payloads and quick tests. Managed uploads are better for large files, since they split work into parts and retry chunks under the hood.
import boto3
s3 = boto3.client('s3')
s3.put_object(Bucket='my-bucket',
Key='reports/2025/inline.txt',
Body=b'Hello from put_object')
IAM And Region Checks
Uploads fail for access and region drift as well. These checks reduce guesswork.
- Confirm the region — Create the client in the same region as the bucket or rely on a session with the right default.
- Grant the right actions — The caller needs
s3:PutObject. If you include encryption or metadata, the policy must allow those headers. - Print the caller identity — Use STS once during debug to spot role mix-ups.
import boto3
sts = boto3.client('sts')
print(sts.get_caller_identity()['Arn'])
Switching From Resource To Client In Place
Sometimes the codebase already passes an S3 resource around. You can still fix the call without refactoring every site that touches it. Reach the low-level client via resource.meta.client and make the same upload call through that handle.
def put_report(s3_resource, src, dest_key):
s3_resource.meta.client.upload_file(src, 'my-bucket', dest_key)
This is a neat bridge during a migration. Later, create one shared boto3.client('s3') and pass it where needed.
Performance Notes And Limits
- Multipart threshold — The managed uploader flips to multipart above a threshold. Tune it with
TransferConfigwhen hosts are memory-tight or links are slow. - open file handles — Always close files after a manual
open(...). The uploader cleans up its own parts, but your process still owns the local handle. - Streaming payloads — With
upload_fileobjyou can pipe data that never touches disk. That reduces wear on small instances and containers.
Smoke Test Checklist
- Create a tiny file — Two lines of text are enough.
- Upload with your final pattern — Client,
Bucket, orObject. - Head the object — Confirm content length and headers match what you set.
- Download once — Read a few bytes to verify content.
- Remove the test key — Keep buckets tidy so audits stay clean.
AttributeError: ‘S3 ServiceResource’ Object Has No Attribute ‘Upload_File’ — Fix Summary
You saw the string AttributeError: ‘S3 ServiceResource’ object has no attribute ‘upload_file’ because the call landed on the wrong object. In boto3, managed uploads belong on a client, a bucket, or an object resource. Pick one of those shapes and keep the method name lowercase. That single change clears the error and gives you a reliable upload path.
To avoid a repeat, add a tiny helper that takes a client and always calls upload_file through it. That keeps method names, retries, and configs in one place while your app code stays tidy.
