S3 InvalidBucketName: Fix "The Specified Bucket Is Not Valid" and "Invalid Bucket Name" (Python, Node.js, CLI, Terraform)

Logeshwaran
—

InvalidBucketName means the text you passed as an S3 bucket name isn't a bucket name S3 or your SDK will accept. It comes in two forms. If S3 itself refuses a CreateBucket call, you see An error occurred (InvalidBucketName) when calling the CreateBucket operation: The specified bucket is not valid., almost always because the name has uppercase letters, underscores, or is too long. If your own code never got as far as S3, Python shows ParamValidationError: Parameter validation failed: Invalid bucket name "s3://jakes-shop-photos" and Node.js shows InvalidBucketName: Bucket name shouldn't contain '/'. Here is the surprise: most of these errors have nothing to do with a badly named bucket. The bucket is fine. Something put a path where only the name belongs, such as s3://jakes-shop-photos copied from the console, or jakes-shop-photos/2026 with a folder attached. The fix for that is to split the path, not to rename anything. And the stranger part: the SDKs happily let Jakes_Shop_Photos through, because S3 still holds buckets with names like that from before 2018.

Jake runs a phone repair shop, and his customers' repair photos live in an S3 bucket called jakes-shop-photos. His friend Ethan, a developer, wrote a small Python script that uploads the day's photos each evening. When Jake moved the upload folder to a new year, he opened the S3 console, clicked the button that copies the S3 URI, and pasted s3://jakes-shop-photos/2026 into the script's settings. The next evening the script failed with "Invalid bucket name." A week later Ethan's build pipeline, which creates a test bucket for each feature branch, failed with "The specified bucket is not valid" on a branch called Feature/Login_Page. Same word, two completely different problems. This page is what Ethan showed Jake: the two kinds of InvalidBucketName and how to tell them apart, the path mistake and a two-line fix in Python and Node.js, the full naming rules with a checker you can run, why SDKs let some invalid names through, names from environment variables, branch names and generated names, and the Terraform, CloudFormation and CLI versions of the same error.

⚡ Quick Answer

• "Invalid bucket name" or "shouldn't contain '/'" → you passed a path; pass only the bucket name. The path mistake.

• "The specified bucket is not valid" → S3 refused the name; check the rules. Naming rules.

• Name from a variable or file → strip spaces and newlines first. Hidden characters.

• Generated names → lowercase them, replace other characters, cut to 63. Branch names.

• Not sure which one you have? → the two kinds look alike but come from different places. Tell them apart.

Want a quick check? The bucket name checker lists every rule a name breaks.

New to S3? Our plain-English guide to Amazon S3 explains buckets, keys and objects in about ten minutes. You can follow this page without it; every step starts from zero.

🧭 NEW HERE? READ THESE FIRST

New to S3 buckets and names? These five pages make the rest of this one easy:

📌 Bookmark this; the two-kinds table and the name checker are the parts you will come back for.

Two errors with one name: tell them apart in ten seconds

"InvalidBucketName" is used by two different checks, and the fix depends on which one stopped you.

The first check runs on your own computer, inside the AWS SDK or CLI, before any request leaves. It looks for characters that can never appear in a bucket name at all, such as /, : and spaces. When it fails, nothing was sent to AWS, so there's no request ID, no HTTP status and no AWS account involved.

The second check runs at S3, mostly when you create a bucket. S3 applies its full naming rules and answers with error code InvalidBucketName and the message "The specified bucket is not valid."

Where you see itExact textWhich checkUsual cause
Python (boto3)ParamValidationError: Parameter validation failed: Invalid bucket name "s3://jakes-shop-photos": Bucket name must match the regex ...On your computerA path or URI passed as the bucket
Node.js (SDK v3)InvalidBucketName: Bucket name shouldn't contain '/', received 's3://jakes-shop-photos'On your computerA path or URI passed as the bucket
AWS CLI (s3api)aws: [ERROR]: An error occurred (ParamValidation): Parameter validation failed: Invalid bucket name ...On your computer--bucket s3://... or --bucket name/folder
Any SDK or CLI, creating a bucketAn error occurred (InvalidBucketName) when calling the CreateBucket operation: The specified bucket is not valid.At S3Uppercase, underscores, length, reserved prefixes
AWS CLI (s3 mb)make_bucket failed: s3://Jakes-Shop-Photos An error occurred (InvalidBucketName) when calling the CreateBucket operation: The specified bucket is not valid.At S3Uppercase letters

Notice the trap in the Node.js row. The JavaScript SDK names its local error InvalidBucketName too, the same code S3 uses. So in Node.js, both checks produce an error called InvalidBucketName, and only the message tells you which one you hit: "shouldn't contain '/'" is local, "The specified bucket is not valid" is S3.

Jake: "So one of these never even reached Amazon."

Ethan: "Right. Think of the shop's parts orders. The first error is the order form refusing to let you type a whole street address in the 'supplier name' box. The second is the supplier reading your form and saying 'we don't have anyone called that.' Same word, 'invalid,' but one never left your counter."

Find which check stopped you, in four steps

  1. Read the message, not the code. A long regex or "shouldn't contain '/'" means the check on your computer. "The specified bucket is not valid" means S3.
  2. Print the exact value. Log the bucket name with quotes around it, or with repr() in Python, so spaces, newlines and empty values show up.
  3. Look for a path. If the value contains s3://, https:// or a /, split it into a bucket and a key; that fixes the local check every time.
  4. If S3 refused it, check the rules. Run the name through the checker below; it lists every rule the name breaks.

Most people are done after step 3. The rest of this page covers each step in detail, starting with the path mistake, because it's behind most of these errors.

The most common cause: a path where the bucket name belongs

Every S3 API call takes the bucket name and the object key as two separate values. The bucket is jakes-shop-photos. The key is 2026/screen.jpg. The S3 URI you copy from the console, s3://jakes-shop-photos/2026/screen.jpg, joins them for humans, and the SDKs don't take it apart for you.

So all of these, passed as the bucket, fail the local check:

  • s3://jakes-shop-photos, an S3 URI
  • jakes-shop-photos/2026, a bucket with a folder attached
  • https://jakes-shop-photos.s3.amazonaws.com, an object URL
  • jakes-shop-photos , with a trailing space
  • An empty string, from a variable that was never set

Here is the Python version of Jake's mistake:

import boto3

s3 = boto3.client("s3")
s3.upload_file("screen.jpg", "s3://jakes-shop-photos/2026", "screen.jpg")
botocore.exceptions.ParamValidationError: Parameter validation failed:
Invalid bucket name "s3://jakes-shop-photos/2026": Bucket name must match the regex "^[a-zA-Z0-9.\-_]{1,255}$" or be an ARN matching the regex "^arn:(aws).*:(s3|s3-object-lambda):[a-z\-0-9]*:[0-9]{12}:accesspoint[/:][a-zA-Z0-9\-.]{1,63}$|^arn:(aws).*:s3-outposts:[a-z\-0-9]+:[0-9]{12}:outpost[/:][a-zA-Z0-9\-]{1,63}[/:]accesspoint[/:][a-zA-Z0-9\-]{1,63}$"

The fix is to put the folder in the key, where it belongs:

s3.upload_file("screen.jpg", "jakes-shop-photos", "2026/screen.jpg")

Split an S3 URI in Python

If your settings, a config file or a user give you an S3 URI, split it once, at the edge of your program, and pass the two parts on. Python's standard library does the work:

from urllib.parse import urlparse


def split_s3_uri(uri):
    """'s3://bucket/path/to/key' -> ('bucket', 'path/to/key')"""
    parts = urlparse(uri)
    if parts.scheme != "s3" or not parts.netloc:
        raise ValueError(f"not an s3:// URI: {uri!r}")
    return parts.netloc, parts.path.lstrip("/")

Here's what it returns:

split_s3_uri("s3://jakes-shop-photos/2026/screen.jpg")   # ('jakes-shop-photos', '2026/screen.jpg')
split_s3_uri("s3://jakes-shop-photos")                    # ('jakes-shop-photos', '')

And Jake's upload becomes:

bucket, prefix = split_s3_uri("s3://jakes-shop-photos/2026")
s3.upload_file("screen.jpg", bucket, f"{prefix}/screen.jpg")

The function refuses anything that isn't an s3:// URI, such as jakes-shop-photos/2026, with a clear ValueError, so a bad setting fails at startup with a message you wrote, not deep inside an upload.

Split an S3 URI in Node.js

In JavaScript, the built-in URL class understands s3:// well enough to do the same job:

function splitS3Uri(uri) {
  const u = new URL(uri);
  if (u.protocol !== "s3:" || !u.hostname) throw new Error(`not an s3:// URI: ${uri}`);
  return { bucket: u.hostname, key: decodeURIComponent(u.pathname.replace(/^\//, "")) };
}
splitS3Uri("s3://jakes-shop-photos/2026/screen.jpg")
// { bucket: 'jakes-shop-photos', key: '2026/screen.jpg' }

The decodeURIComponent matters for keys with spaces. URL encodes a space as %20 in the path, and S3 keys are stored with the real space, so s3://jakes-shop-photos/repair photos/a.jpg comes back as the key repair photos/a.jpg, as it should.

In the AWS CLI: s3 takes URIs, s3api takes names

The CLI has two families of S3 commands, and they want different things. The high-level aws s3 commands (ls, cp, sync, mb, rb) take s3:// URIs. The low-level aws s3api commands take a bucket name and a key as separate options:

aws s3 cp screen.jpg s3://jakes-shop-photos/2026/screen.jpg
aws s3api put-object --bucket jakes-shop-photos --key 2026/screen.jpg --body screen.jpg

Mixing them up is the CLI version of Jake's mistake:

aws s3api head-bucket --bucket s3://jakes-shop-photos
aws: [ERROR]: An error occurred (ParamValidation): Parameter validation failed:
Invalid bucket name "s3://jakes-shop-photos": Bucket name must match the regex ...

Jake: "I copied it straight from Amazon's own console. How is that the wrong name?"

Ethan: "The console gave you the full address, street and room number together. The SDK only wants the street. Split it once when the script starts, and the rest of the code never sees a URI again."

Names from environment variables and files: the invisible characters

Bucket names usually come from configuration: an environment variable, a .env file, a parameter store value, a line in a text file. Each of those can carry characters you can't see.

  • A trailing space, from copying a value out of a web page or a document. The local check catches it: Invalid bucket name "jakes-shop-photos ". Look closely at the closing quote; the space sits just before it.
  • A trailing newline, from reading a file with open(...).read() or from a shell command's output. This one is sneakier: Python's validation pattern ends with $, which also matches just before a final newline, so "jakes-shop-photos\n" passes botocore's check and the request goes to AWS with the newline in it, where it fails with a different and less helpful error.
  • An empty value, when the variable isn't set in this environment. The check reports Invalid bucket name "", with the two quotes side by side.
  • Quotes that became part of the value, such as BUCKET="jakes-shop-photos" in a file read by a tool that doesn't strip quotes, giving a name that literally starts and ends with a quote mark.

The habit that prevents all four: clean and check the value once, when your program starts.

import os

bucket = os.environ.get("PHOTO_BUCKET", "").strip().strip('"').strip("'")
if not bucket:
    raise SystemExit("PHOTO_BUCKET is not set")

A repr() in your error message makes invisible characters visible too: print(repr(bucket)) shows 'jakes-shop-photos\n' where a plain print shows nothing odd.

In a shell, od -c does the same job for an environment variable. Every character is printed separately, so a trailing newline shows up as \n at the end:

printf '%s' "$PHOTO_BUCKET" | od -c
0000000   j   a   k   e   s   -   s   h   o   p   -   p   h   o   t   o
0000020   s  \n

The S3 bucket naming rules, all of them

When the local check passes but S3 says "The specified bucket is not valid," the name broke one of S3's naming rules. For new general purpose buckets, those are:

  • Between 3 and 63 characters long.
  • Only lowercase letters, numbers, periods (.) and hyphens (-).
  • Must begin and end with a letter or a number.
  • No two periods in a row.
  • Not formatted as an IP address, such as 192.168.5.4.
  • Must not start with xn--, sthree- or amzn-s3-demo-.
  • Must not end with -s3alias, --ol-s3, .mrap, --x-s3 or --table-s3. Those endings are reserved for access point aliases, Object Lambda access point aliases, Multi-Region Access Points, directory buckets and S3 Tables buckets.
  • Must end with -an only when you're creating a bucket in your account regional namespace, which uses its own naming pattern with your account ID and Region.
  • Buckets used with S3 Transfer Acceleration can't contain periods.

Here's how some real names fare:

NameValid?Why
jakes-shop-photosYesLowercase, hyphens, starts and ends with a letter
www.jakesrepair.comYesPeriods are allowed, with caveats below
Jakes-Shop-PhotosNoUppercase letters
jakes_shop_photosNoUnderscores
jpNoShorter than 3 characters
jakes-shop-NoEnds with a hyphen
jakes..photosNoTwo periods in a row
192.168.5.4NoLooks like an IP address
jakes-photos-s3aliasNoReserved ending

Passing every rule doesn't guarantee you get the name. Bucket names are shared across all AWS accounts in a partition, so a valid name someone else already owns fails with a different error, BucketAlreadyExists. Our guide to "Bucket name already exists" covers that error and naming schemes that avoid collisions.

Periods: valid, but they break HTTPS

Names with periods, like www.jakesrepair.com, are valid and are the traditional choice for S3 static websites. But S3's wildcard TLS certificate only covers one level of name, so HTTPS requests to www.jakesrepair.com.s3.amazonaws.com fail certificate checks, and Transfer Acceleration refuses such buckets entirely. That shows up as an SSL or certificate error, not as InvalidBucketName. For anything other than a static website, use hyphens instead of periods.

Why the rules exist: your bucket name is a web address

The rules look fussy until you see where the name ends up. Every bucket is reachable at a web address with the name as the first part of the host name, such as https://jakes-shop-photos.s3.us-east-1.amazonaws.com/2026/screen.jpg. Host names follow the internet's naming rules: they don't distinguish uppercase from lowercase, they can't contain underscores, each part between periods has a length limit, and a name that looks like an IP address is confusing. S3's rules are mostly those internet rules, applied up front, so every new bucket name works as a host name. The legacy buckets with uppercase names predate this, which is why they can only be reached with the older path-style addresses.

How many buckets you can have, and why "a bucket inside a bucket" isn't a thing

Each AWS account can create up to 10,000 general purpose buckets by default, and you can ask for more through Service Quotas; bucket quotas for the standard Regions are viewed and managed from US East (N. Virginia). There's no limit on the number of objects in one bucket or on a bucket's total size, so one bucket with prefixes is usually better than many small buckets.

And you can't create a bucket inside another bucket. That's the deeper reason jakes-shop-photos/2026 can never be a bucket name: the part after the slash is always a prefix inside the bucket, never a bucket of its own. If you need separate areas, use prefixes and give each one its own permissions in the bucket policy.

Check a name before you create the bucket

This function applies every rule above and tells you which ones a name breaks. It uses only Python's standard library, so you can run it anywhere, including in a build step before you try to create a bucket:

import ipaddress
import re


def bucket_name_problems(name):
    """Return the S3 naming rules a new general purpose bucket name breaks (empty list = valid)."""
    problems = []
    if not 3 <= len(name) <= 63:
        problems.append("must be 3 to 63 characters long")
    if re.search(r"[^a-z0-9.-]", name):
        problems.append("only lowercase letters, numbers, periods and hyphens are allowed")
    if not re.match(r"^[A-Za-z0-9].*[A-Za-z0-9]$", name):
        problems.append("must begin and end with a letter or number")
    if ".." in name:
        problems.append("must not contain two adjacent periods")
    try:
        ipaddress.IPv4Address(name)
        problems.append("must not be formatted as an IP address")
    except ValueError:
        pass
    for prefix in ("xn--", "sthree-", "amzn-s3-demo-"):
        if name.startswith(prefix):
            problems.append(f"must not start with {prefix}")
    for suffix in ("-s3alias", "--ol-s3", ".mrap", "--x-s3", "--table-s3"):
        if name.endswith(suffix):
            problems.append(f"must not end with {suffix}")
    return problems

A few results:

bucket_name_problems("jakes-shop-photos")     # []
bucket_name_problems("Jakes_Shop_Photos")     # ['only lowercase letters, numbers, periods and hyphens are allowed']
bucket_name_problems("jakes..photos")         # ['must not contain two adjacent periods']
bucket_name_problems("192.168.5.4")           # ['must not be formatted as an IP address']
bucket_name_problems("jakes-photos-s3alias")  # ['must not end with -s3alias']

It checks the rules for new general purpose buckets in the shared namespace. Directory buckets and buckets in an account regional namespace have their own suffix formats, and an empty list still doesn't promise the name is free; only S3 can answer that.

Why the SDKs let invalid names through

Look again at botocore's pattern: ^[a-zA-Z0-9.\-_]{1,255}$. It allows uppercase letters, underscores and names up to 255 characters, all things S3 refuses for new buckets. That isn't a mistake. Before March 1, 2018, buckets created in the US East (N. Virginia) Region could have names up to 255 characters long with uppercase letters and underscores. Many of those buckets still exist and are still used every day, so the SDKs have to let you read from and write to them.

The result is a gap. The local check only rejects names that can't belong to any bucket, old or new. Names that could belong to an old bucket, such as Jakes_Shop_Photos, pass the local check and go to S3. What happens next depends on the call:

  • CreateBucket refuses them with InvalidBucketName, because no new bucket may have that name.
  • Calls on an existing bucket work if a legacy bucket with that exact name exists and you have access to it. Otherwise S3 refuses them, and the error comes from S3, not from your SDK.

The JavaScript SDK v3 checks even less on its own side: it rejects a value containing / unless it's an ARN, and leaves everything else to S3. So in Node.js, uppercase names, underscores and even spaces reach S3.

Jake: "So the check on my side is loose on purpose."

Ethan: "It has to work for every bucket that has ever existed, including ones made under the old rules. The strict rules only apply when you're making a new one, and only S3 enforces those."

Names built from branch names, project titles and IDs

Ethan's pipeline built a bucket name from the branch it was testing: jakes-shop- plus the branch name. A branch called Feature/Login_Page produced jakes-shop-Feature/Login_Page, which breaks three rules at once. Any name built from text you don't control needs cleaning first: branch names, user names, project titles, ticket IDs.

This function turns any label into something S3 accepts: lowercase, every run of other characters replaced with one hyphen, no hyphens at either end, and no longer than 63 characters:

import re


def to_bucket_name(text, max_len=63):
    """Turn any label (a branch name, a project title) into something S3 accepts."""
    name = re.sub(r"[^a-z0-9-]+", "-", text.strip().lower())
    name = re.sub(r"-{2,}", "-", name).strip("-")
    return name[:max_len].rstrip("-")
to_bucket_name("Feature/Login_Page")        # 'feature-login-page'
to_bucket_name("  Jake's Phone Repair! ")   # 'jake-s-phone-repair'
to_bucket_name("release-2026.10")           # 'release-2026-10'

It replaces periods too, which avoids the HTTPS problem above. When you add a prefix and a suffix around a cleaned value, leave room for them: jakes-shop- plus a 63-character branch name is already too long. Budget the length: max_len=63 - len(prefix) - len(suffix).

Jake: "So the pipeline wasn't broken. My branch name was."

Ethan: "Your branch name was fine for Git. Git allows slashes and capitals; S3 doesn't. The cleaning step translates between the two, and the rule check after it is the proofreader."

Two more habits keep generated names out of trouble:

  • Check before creating. Run the result through bucket_name_problems() and fail the build with a clear message if the list isn't empty.
  • Add something unique. Two branches can clean up to the same name, and other AWS accounts may own the obvious ones. A short random or account-based suffix avoids both collisions.

InvalidBucketName in Terraform, CloudFormation and CDK

Terraform

In Terraform, InvalidBucketName almost always comes from a bucket argument assembled from variables, like a workspace, a branch or an environment name that contains uppercase letters. Clean the value with Terraform's own functions:

variable "branch" {
  type = string
}

resource "aws_s3_bucket" "test" {
  bucket = "jakes-shop-${substr(replace(lower(var.branch), "/[^a-z0-9-]+/", "-"), 0, 40)}"
}

For a branch called Feature/Login_Page, the inner expression gives feature-login-page. lower() handles the uppercase, replace() with a /.../ pattern turns every other character into a hyphen, and substr() keeps the total under 63. If you don't need a predictable name at all, use bucket_prefix instead of bucket and let Terraform add a unique suffix, but keep the prefix short and lowercase.

CloudFormation and CDK

The BucketName property of AWS::S3::Bucket follows the same rules. The simplest fix is to leave BucketName out, so CloudFormation generates a valid, unique name, and refer to it with !Ref wherever you need it. In CDK, that's the default when you don't pass bucketName. If you must set a name, build it from lowercase parameters and avoid stack names with uppercase letters, which are allowed for stacks but not for buckets.

Access points, ARNs and aliases in the Bucket parameter

Not everything in the Bucket parameter has to be a plain bucket name. Botocore's check also accepts S3 access point ARNs, such as arn:aws:s3:us-east-1:111122223333:accesspoint/photos-ap, and access point aliases, which look like bucket names and end in -s3alias. That's why -s3alias is a reserved ending for buckets.

Two mistakes are common here. Passing a bucket ARN, arn:aws:s3:::jakes-shop-photos, fails the local check, because only access point ARNs are accepted; pass the plain bucket name instead. And an access point name on its own, photos-ap, isn't a bucket, so S3 treats it as a bucket that doesn't exist; use the ARN or the alias.

Globally unique names, and the account regional namespace

Ordinary bucket names live in one shared namespace for the whole partition: every AWS account in the standard Regions competes for the same names, and so do accounts within the China, GovCloud (US) and European Sovereign Cloud partitions, each among themselves. That's why a valid, sensible name can still be taken, and why deleting a bucket lets anyone else claim its name.

S3 now offers a second option, an account regional namespace, where only your account can ever create a name. Names there follow a fixed pattern: your own prefix, then your 12-digit account ID, the Region code, and -an:

jakes-shop-photos-111122223333-us-west-2-an

You create one by adding --bucket-namespace account-regional to create-bucket:

aws s3api create-bucket \
  --bucket jakes-shop-photos-111122223333-us-west-2-an \
  --bucket-namespace account-regional \
  --region us-west-2 \
  --create-bucket-configuration LocationConstraint=us-west-2

The suffix has to be your own account ID and the Region you create the bucket in; S3 refuses a mismatch. The -an ending is reserved for this namespace, so an ordinary bucket can't end with it. The prefix still follows the normal rules: lowercase letters, numbers, hyphens, and the whole name within 63 characters. That budget is tighter than it looks, because the suffix alone takes over 20 characters.

Directory buckets: the name needs a zone suffix

Directory buckets, the high-performance S3 Express One Zone kind, follow their own pattern: a base name, then the ID of the Availability Zone or Local Zone, then --x-s3:

jakes-shop-cache--usw2-az1--x-s3

The console adds that suffix for you. The API and CLI don't: when you create a directory bucket with code, you must write the full name including the zone ID, and leaving the suffix off gets the name refused. The rest of the rules match general purpose buckets, with one difference: no periods, only lowercase letters, numbers and hyphens, 3 to 63 characters including the suffix. And because --x-s3 is reserved for directory buckets, an ordinary bucket can't end with it.

How to find your exact bucket name

Many InvalidBucketName errors start with not knowing the bucket's exact name and typing something close. Three ways to get it exactly right:

  • List your buckets. aws s3 ls prints every bucket in the account, one per line, with the date it was created. Copy the name from the last column.
  • Read it from an S3 URI. In s3://jakes-shop-photos/2026/screen.jpg, the bucket is the part between s3:// and the next slash. Everything after that slash is the key.
  • Read it from an object URL. In https://jakes-shop-photos.s3.us-east-1.amazonaws.com/2026/screen.jpg, the bucket is the first part of the host name, before .s3..

That last part of an S3 URI, the key, often has a "folder" in it, like 2026/. S3 calls that a prefix. It's part of the object's key, not part of the bucket, which is exactly why it belongs in the Key parameter and why putting it in Bucket causes this error.

You can't rename a bucket: here's the move instead

If a bucket's name is wrong, uppercase in an old us-east-1 bucket or just badly chosen, S3 has no rename. A bucket's name and Region are fixed when you create it. The move is three steps:

  1. Create the new bucket with a valid name: aws s3 mb s3://jakes-shop-photos-2.
  2. Copy everything across with a bucket-to-bucket sync, which copies inside S3 without downloading anything: aws s3 sync s3://jakes-shop-photos-old s3://jakes-shop-photos-2.
  3. Point everything at the new name: code, environment variables, bucket policies, IAM policies, CloudFront origins, event notifications. Only when nothing uses the old bucket, empty and delete it with aws s3 rb s3://jakes-shop-photos-old --force.

Settings don't come along with the copy. Versioning, encryption defaults, lifecycle rules, the bucket policy and CORS all have to be set again on the new bucket. And think twice before deleting the old one: once a name is free, another AWS account can create a bucket with it and receive any requests still pointed there. Emptying the old bucket and keeping it, with a policy that blocks access, closes that gap. Our guide to bucket names has more on why freed names are risky.

Errors that look like InvalidBucketName but aren't

  • BucketAlreadyExists: the name is valid, but another account owns it. Pick a different name. See our "Bucket name already exists" guide.
  • NoSuchBucket: the name is valid and nobody in this partition owns it, or it's in a different Region than you think. Our NoSuchBucket guide walks through the checks.
  • PermanentRedirect: the bucket exists in another Region. Our PermanentRedirect guide shows how to find it.
  • NoSuchKey: the bucket is right and the object isn't there, often because the folder went into the bucket name and then got removed from the key too. See our NoSuchKey guide.
  • SSL certificate errors with a dotted bucket name: the name is valid; HTTPS can't validate it. See the periods note.

Catching both kinds in your code

Because the two kinds come from different places, they're different exception types in Python. The local one is botocore.exceptions.ParamValidationError; the S3 one is a ClientError with the code InvalidBucketName. Catch both where you create buckets from generated names:

import boto3
from botocore.exceptions import ClientError, ParamValidationError

s3 = boto3.client("s3", region_name="us-east-1")


def create_bucket(name):
    try:
        s3.create_bucket(Bucket=name)
    except ParamValidationError as err:
        raise SystemExit(f"not a bucket name at all (check for paths or spaces): {err}")
    except ClientError as err:
        if err.response["Error"]["Code"] == "InvalidBucketName":
            raise SystemExit(f"S3 refused the name {name!r}: {err.response['Error']['Message']}")
        raise

Better still, call bucket_name_problems() before create_bucket and stop with the list of broken rules. Our guide to boto3 error handling explains ClientError and ParamValidationError in depth.

Still stuck? Troubleshooting by symptom

The error shows a regex

That's the local check in Python or the CLI. Look at the name inside the quotes: it contains a /, :, a space or nothing at all. Split the S3 URI or fix the variable.

"Bucket name shouldn't contain '/'" in Node.js

The JavaScript SDK's local check. You passed a URI or a bucket with a folder attached. Use the splitS3Uri function above and put the folder in Key.

It works locally and fails in CI

The name is built from something that differs in CI: a branch name, a workspace, an environment variable with uppercase letters or a trailing newline. Print it with repr() in the pipeline log and clean it with to_bucket_name() or Terraform's lower() and replace().

An old bucket with uppercase letters works, but I can't create one like it

Legacy buckets created in US East (N. Virginia) before March 1, 2018 may keep names that break today's rules. New buckets can't have them anywhere.

The name passes every rule and creation still fails

Read the error code. BucketAlreadyExists means someone else owns it; BucketAlreadyOwnedByYou means you do; IllegalLocationConstraintException is about the Region, not the name.

S3 InvalidBucketName: frequently asked questions

What does InvalidBucketName mean in S3?

S3 refused the text you gave as a bucket name, usually when creating a bucket with uppercase letters, underscores or a name outside 3 to 63 characters. The full message is "The specified bucket is not valid." The JavaScript SDK also uses InvalidBucketName for its own local check on names containing a slash.

What does "The specified bucket is not valid" mean?

It's S3's message for the InvalidBucketName error code. The name you sent breaks S3's naming rules for new buckets: only lowercase letters, numbers, periods and hyphens, 3 to 63 characters, starting and ending with a letter or number.

Why does boto3 say Invalid bucket name?

Boto3 checked the name on your computer and found characters no bucket can have, usually because you passed an S3 URI like s3://bucket or a path like bucket/folder. Pass only the bucket name, and put the folder in the key.

How do I fix "Bucket name shouldn't contain '/'"?

That's the JavaScript SDK v3 rejecting a bucket value with a slash. Split the S3 URI: with new URL(uri), the hostname is the bucket and the path, without its leading slash, is the key.

Can an S3 bucket name have uppercase letters?

Not for new buckets. Names must be lowercase. Buckets created in US East (N. Virginia) before March 1, 2018 could use uppercase letters and underscores, and some still exist, which is why SDKs don't block uppercase names on their own.

Can an S3 bucket name have underscores?

No, not for new buckets. Use hyphens instead. Only legacy buckets from before March 1, 2018 in US East (N. Virginia) can have underscores.

How long can an S3 bucket name be?

Between 3 and 63 characters for new buckets. Legacy US East (N. Virginia) buckets created before March 1, 2018 could be up to 255 characters, which is why botocore's own check allows 255.

Can an S3 bucket name have periods?

Yes, but periods break HTTPS with virtual-hosted-style URLs and aren't allowed with Transfer Acceleration. They're fine for static website buckets; use hyphens everywhere else.

Can I pass an s3:// URI to boto3?

No. Boto3 calls take the bucket and key separately. Split the URI with urllib.parse.urlparse: netloc is the bucket, and path without its leading slash is the key.

Why does my bucket name from an environment variable fail?

It probably has a trailing space or newline, surrounding quotes, or it's empty because the variable isn't set. Use .strip() when you read it, and print it with repr() to see hidden characters.

What characters are allowed in an S3 bucket name?

Lowercase letters a to z, numbers 0 to 9, periods and hyphens. The name must start and end with a letter or number and can't have two periods in a row.

What bucket name prefixes and suffixes are reserved?

Names can't start with xn--, sthree- or amzn-s3-demo-, and can't end with -s3alias, --ol-s3, .mrap, --x-s3 or --table-s3. The -an ending is only for buckets in your account regional namespace.

How do I make a valid bucket name from a Git branch name?

Lowercase it, replace every run of characters other than letters, numbers and hyphens with one hyphen, trim hyphens from both ends and cut it to fit within 63 characters including your prefix. In Terraform: lower(), replace() with a regex, and substr().

Is InvalidBucketName the same as BucketAlreadyExists?

No. InvalidBucketName means the name breaks the naming rules. BucketAlreadyExists means the name is valid but another AWS account already owns it, so you need a different name.

Can I use a bucket ARN as the Bucket parameter?

No. Botocore accepts access point ARNs and access point aliases in the Bucket parameter, but not bucket ARNs like arn:aws:s3:::my-bucket. Pass the plain bucket name.

How do I check if a bucket name is valid before creating it?

Test it against the naming rules with a small function like bucket_name_problems() on this page. A valid name can still be taken by another account, so handle BucketAlreadyExists when you create it.

Are S3 bucket names globally unique?

Yes, within a partition: every AWS account in the standard Regions shares one namespace, so a name anyone already owns is unavailable to you. Buckets in your account regional namespace, named prefix-accountid-region-an, can only ever be created by your account.

How many S3 buckets can I create?

Up to 10,000 general purpose buckets per AWS account by default, and you can request more through Service Quotas, managed from US East (N. Virginia) for the standard Regions. There's no limit on how many objects one bucket holds.

Can I rename an S3 bucket?

No. A bucket's name and Region are fixed at creation. Create a new bucket with the name you want, copy the objects with aws s3 sync between the two buckets, move every reference and setting over, then empty and delete the old bucket.

How do I find my S3 bucket name?

Run aws s3 ls to list every bucket in the account. In an S3 URI, the bucket is the part between s3:// and the next slash; in an object URL, it's the first part of the host name, before .s3.

What is a prefix in an S3 bucket?

It's the folder-like start of an object's key, such as 2026/ in 2026/screen.jpg. It belongs to the key, not the bucket name, so it goes in the Key parameter; putting it in Bucket causes the Invalid bucket name error.

Why does aws s3 mb fail with InvalidBucketName?

The name after s3:// breaks the naming rules, most often because of uppercase letters, as in s3://Test-Bucket. Use only lowercase letters, numbers and hyphens, 3 to 63 characters.

For readers preparing for an AWS certification

We have been covering AWS certifications alongside our fixes, so here is a short extra for readers studying for one. If you only came for the fix, you already have it; feel free to skip this part.

Exams that test this topic: Developer – Associate (DVA-C03 from December 1, 2026) • Cloud Practitioner.

Here are the exam-style questions this topic tends to raise, with the reasoning you need to answer them:

1. Which bucket names cause InvalidBucketName?

Names with uppercase letters, underscores, adjacent dots, IP-address format, fewer than 3 or more than 63 characters, or reserved prefixes and suffixes such as xn-- or -s3alias.

2. Why are dots discouraged in bucket names even though they are allowed?

With virtual-hosted-style HTTPS, a dotted name breaks the wildcard certificate match, and Transfer Acceleration does not support dotted names.

3. Does the ARN of a bucket include the Region or account?

No. A general purpose bucket ARN is arn:aws:s3:::bucket-name, because names are global within the partition.

4. A CloudFormation template passes a parameter with a trailing space as the bucket name. What happens?

The create fails with InvalidBucketName. Validate parameters with an AllowedPattern.

5. Are access point names under the same rules?

Access points have their own naming rules and are unique per account and Region, not globally.

6. How should you name buckets for many environments?

Include the environment, Region and account ID, for example app-logs-prod-123456789012-us-east-1, to stay unique and readable.

Exam codes and dates change often. Our AWS certification changes page keeps track of what is current.

πŸ“š ALSO READ

Where to go next, from the other S3 errors that look like this one to handling them all in boto3:

📌 Bookmark this; the two-kinds table and the name checker apply to every bucket you will ever create.

Jake's upload script now reads one setting, the S3 URI he copies from the console, and splits it into a bucket and a folder on the first line. It hasn't failed since, through two folder changes. Ethan's pipeline runs every branch name through the cleaning function, checks the result against the rules, and adds a short suffix, so Feature/Login_Page became jakes-shop-feature-login-page-7f3a and the tests ran. Ethan wrote the line below in the project's README, under a heading Jake had suggested: "Before you blame the bucket."

📌 If you keep one line from this page

The Bucket parameter takes a name, never a path: split s3:// URIs once, and lowercase anything you build a bucket name from.

A regex or "shouldn't contain '/'" means your code stopped it; "The specified bucket is not valid" means S3 did.

Revision note. Written October 11, 2026, for everyone who pasted a perfectly good S3 address into the wrong box. "A place for everything, and everything in its place," says the old proverb; the bucket goes in one box, the folder in the other.

Related