Resource Cleanup & Teardown Rules
Cleanup failures are quiet. Nothing crashes; a connection leaks, a stream stays open, or a resource lingers and keeps billing.
Search across all documentation pages
Cleanup failures are quiet. Nothing crashes; a connection leaks, a stream stays open, or a resource lingers and keeps billing.
These rules cover the two kinds of cleanup: releasing the client-side resources an SDK call uses, and tearing down the AWS resources your code creates.
GetObject body holds a socket open; read it fully or close it so the connection returns to the pool.with block so it closes even on error.Body stream (for example via transformToString/transformToByteArray) or destroy it so the socket is freed.finally or context manager.Read the body inside a context manager so the stream is closed even on error.
# --- Python (boto3) ---
resp = s3.get_object(Bucket="reports", Key="q3.csv")
with resp["Body"] as body: # closes the stream on exit, even on error
data = body.read()// --- TypeScript (AWS SDK v3) ---
import { GetObjectCommand } from "@aws-sdk/client-s3";
const resp = await s3.send(new GetObjectCommand({ Bucket: "reports", Key: "q3.csv" }));
// Consuming the stream frees the underlying socket back to the pool.
const data = await resp.Body?.transformToString();client.destroy() to close sockets when a client will not be used again, for example at process shutdown.Destroy a v3 client you will not reuse; keep and reuse a boto3 client.
# --- Python (boto3) ---
import boto3
# Reuse one client for the process; do not build a new one per call.
s3 = boto3.client("s3", region_name="us-east-1")
# ... many calls on the same client ...// --- TypeScript (AWS SDK v3) ---
import { S3Client } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: "us-east-1" });
// ... many calls ...
s3.destroy(); // close sockets when this client will not be used againDeleteBucket fails on a non-empty bucket; delete or expire objects first.An unread GetObject body keeps its socket open. Until you read or close it, that connection is not returned to the pool, which can exhaust connections under load.
They do not require an explicit close, but you should reuse one client rather than creating many. A fresh client per call leaks connection pools over time.
When the client will not be used again, such as at process shutdown or after a one-off task. destroy() closes the underlying sockets.
At module scope, outside the handler. Warm invocations then reuse the same client instead of constructing and abandoning one on every request.
Because dependents block their dependencies. You must remove objects before a bucket and items before a table, or the delete call fails.
Parts of a large upload that never completed. They are stored and billed until aborted, so clean them up explicitly or add a lifecycle rule to expire them.
Make it idempotent. Ignore "does not exist" errors during cleanup so a run that partially failed can be re-torn-down without erroring.
Prefer IaC for infrastructure teardown. A stack destroy removes everything reliably, whereas application-code cleanup easily misses resources and leaves orphans.
S3 lifecycle rules and DynamoDB TTL. They let the service delete expired data for you instead of running and billing a scheduled cleanup job.
Tag SDK-created resources with an owner and purpose. A periodic sweep can then list and remove anything untagged or past its intended lifetime.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 23, 2026