Skip to content

We use analytics cookies to see how the site is used. They are only set if you accept. Accepting also turns on session recording (pages viewed, clicks and scrolls) so we can fix problems; what you type stays hidden. Read the privacy policy

CodeTrendy
Three package names and what the registry compares: json.stream and serde-json match taken names, fit-map is refused as too similar

Developer Tools - 9 min read

Is That Package Name Really Free? How Registries Decide Who Gets a Name

A 404 from the registry only says no package has that exact name. npm, PyPI and crates.io also compare a normalized form and block reserved names, npm and PyPI refuse names that look too similar, and a taken name changes hands only in narrow cases. The rules side by side, the lookup commands, and the order to check a name in.

free /friː/ adj.

of a package name

  1. No package on the registry has this exact name. The only sense a lookup checks.
  2. Passes the registry's format rules.
  3. Matches nothing once the registry normalizes both names.
  4. Not reserved, blocked or too similar to a taken name. On npm, only the first publish checks this.

The registry said 404, npm publish --dry-run passed, and the real publish still came back 403 Forbidden - Package name too similar to existing package: that is how a developer described it in npm CLI issue 9188 in April 2026, adding that by then a developer has usually named the repository and written the README.

Short answer: a package name is free only when four things hold. The registry has no package by that exact name, the name passes the registry's format rules, nothing already published matches it once the registry normalizes both names, and no reserved, blocked or too-similar name stands in the way. A lookup answers only the first question. Check the other three, and the GitHub name, domain and trademark that go with it, before anything public uses the name.

Two words come up throughout. Normalization is the rewrite a registry applies to a name before comparing it, such as lowercasing it or removing punctuation. Name squatting is registering a name only to hold it for later; npm, PyPI, crates.io and GitHub all forbid it.

What does a registry compare when you claim a name?

None of the three big registries compares your name letter for letter. Each one rewrites it first, and two names that end up the same after the rewrite count as one name.

npm

Removes the punctuation and compares what is left (npm blog, 2017).

You typejson.stream

It comparesjsonstream

jsonstream exists, so npm names json.stream and json_stream as names no one can publish; both answer 404 today.

PyPI

Lowercases the name and turns every run of ., - and _ into one - (name normalization spec).

You typeFriendly._Bard

It comparesfriendly-bard

The spec lists Friendly-Bard, friendly.bard and FrIeNdLy-._.-bArD as the same name.

crates.io

Detects collisions without regard to case and treats - and _ as the same (Cargo Book, registry index).

You typeserde-json

It comparesserde_json

Asking the crates.io API for serde-json returns the existing crate serde_json (checked 24 October 2026).

npm's similarity check goes further than punctuation. Its own guidelines ask that an unscoped name "is not spelled in a similar way to another package name" (npm Docs, package name guidelines), and the npm docs do not describe how the check works.

What are the name rules on npm, PyPI and crates.io?

The format rules decide whether a name can exist at all; the last three rows decide whether you can have it. Each cell comes from the registry's own documentation, linked under the table.

RulenpmPyPIcrates.io
CharactersURL-safe characters only; no spaces, no ~)('!*; unscoped names cannot start with ., _ or -ASCII letters, digits, ., _ and -; must start and end with a letter or digitASCII letters, digits, - and _; the first character must be a letter
Length214 characters at most, the scope includedThe name spec sets no length64 characters at most
CaseNew names must be lowercaseAny case, compared lowercasedAny case, compared without regard to case
What is comparedThe name without punctuation, plus a similarity check on unscoped namesThe normalized name, plus a similarity checkThe name without case, with - and _ treated as one
Blocked up frontNode.js core module names such as http and stream, and reserved names such as node_modulesStandard library module names (Python 2.6 on), names the admins prohibit such as requirements.txtReserved names and Windows special names such as nul
SquattingA package with "no genuine function" counts as squatted; such accounts "may be removed or renamed without notice"A package with "no functionality or is empty" is invalid and removedA crate that only reserves a name for a prolonged period, without genuine functionality or significant development activity, breaks the usage policy and may be deleted
Getting a taken nameNot on request; a trademark or other intellectual property claim is "the only path we act on"Under PEP 541: an abandoned project, never "against the wishes of a reachable owner", or the PSF acting on an infringement claimWith "the explicit approval of the current owner"; the team may also delete crates that break its policies

Sources. npm: package.json docs, validate-npm-package-name, moniker rules, Username Policy. PyPI: names and normalization, Choosing a Project Name, Name Retention. crates.io: Cargo manifest format, registry index, crates.io policies.

What does a too-similar refusal look like?

On 12 June 2026 a release workflow published a scoped package fine, then failed on a new unscoped launcher named fit-map. The maintainers recorded the registry's answer in a public GitHub issue:

npm error 403 403 Forbidden - PUT https://registry.npmjs.org/fit-map - Package name
too similar to existing package is-map; try renaming your package to
'@dickolsson/fit-map' and publishing with 'npm publish --access=public' instead

Three things stand out. fit-map and is-map differ in letters, not only in punctuation, so the check is wider than the 2017 rule. The name did not exist on the registry, so a registry lookup said it was free. And the registry itself pointed to the way out: the same name under the publisher's scope.

The rule is also why some old names look like exceptions. reactnative is still on the registry, with a first publish date of 5 March 2015; npm's rule covers new packages only, "a package that has not been in the registry before", so names that came first stay.

How do you check a package name from the command line?

These lookups need no account. Each answers one question, and the comments say which. The 404 for json.stream is real (checked 24 October 2026), and still npm's 2017 post names it as a name no one can publish.

# npm: 404 means no package has exactly this name.
# It does not run the similarity check that the first publish runs.
curl -s -o /dev/null -w "%{http_code}\n" https://registry.npmjs.org/json.stream

# npm: format rules, with the validator library the npm team maintains.
# json.stream passes; JSON.stream fails for new packages (capital letters).
npm install --no-save validate-npm-package-name
node -e 'console.log(require("validate-npm-package-name")(process.argv[1]))' JSON.stream

# PyPI: the JSON API answers for the normalized name too
curl -sL -o /dev/null -w "%{http_code}\n" https://pypi.org/pypi/Python.Dateutil/json

# crates.io: the API answers for the - and _ variant of a taken name
curl -s -A "name-check ([email protected])" https://crates.io/api/v1/crates/serde-json | head -c 200

# Normalize a name the PyPI way before you compare it with anything
python3 -c 'import re,sys; print(re.sub(r"[-_.]+", "-", sys.argv[1]).lower())' Friendly.Bard

crates.io asks API clients for at most one request per second and a User-Agent that identifies the application (crates.io data access policy); put your own contact in place of the example. No public endpoint runs npm's similarity check before you publish, which is what issue 9188 asks for.

Which beliefs about free names are wrong?

A 404 from the registry means you can have the name.

It means no package has that exact name. PyPI says a name can be unavailable "even if a project doesn't appear on PyPI": standard library conflicts, similar names, prohibited names and names registered without a release (PyPI Docs).

A name nobody uses can be claimed if you ask the registry.

npm does "not transfer package, organization, or username ownership simply because another user wants the name"; GitHub does not release usernames "on the basis that they appear inactive or unused"; PyPI needs an owner who cannot be reached, no release in twelve months and no activity on the home page, all three (npm, GitHub, PyPI).

Publishing an empty placeholder keeps the name safe until launch.

That is the squatting every registry in the table forbids, and the placeholder can be removed. Publish when the package does something, or publish under your scope first.

A name that is free on the registry is free to use as a brand.

A trademark claim (or, on npm, another intellectual property claim) is the one request npm and GitHub act on to move a taken name (npm, GitHub), and PyPI's policy names trademark violations as a reason to remove a project (PyPI).

In what order should you check a new name?

Cheapest checks first, and nothing public until the last one passes. A rename after launch costs more than any of these steps.

  1. Run the format rules for every registry you will publish to

    A name that is valid on PyPI can fail on crates.io (a leading digit) or on npm (an uppercase letter).

  2. Normalize the name the way each registry does, then look it up

    Use the commands above. A 200 for any variant means the name is taken.

  3. Decide whether you need an unscoped npm name at all

    npm's guidelines put the similarity rule on unscoped names, and a scope "allows you to create a package with the same name as a package created by another user or organization" (npm Docs, About scopes). Scoped packages are private by default, so the first public publish needs --access public.

  4. Check the GitHub user or organization name

    GitHub names are "first-come, first-served", and "accounts with no visible activity may be in active use" (GitHub Username Policy).

  5. Look up the domain

    ICANN Lookup shows whether a domain is registered and by which registrar.

  6. Search trademarks in the markets you sell to

    USPTO trademark search for the United States, TMview for the EU and its partner offices, WIPO Global Brand Database for international filings. A search is not legal advice; it finds the obvious conflicts early.

  7. Publish a working first version

    On npm, only this step runs the similarity check. Publish something that installs and runs, so the name is used, not squatted.

Questions developers ask about package names

Does a scoped npm name avoid the too-similar check?

npm's guidelines list the similarity rule only for unscoped packages, and in the refusal above the registry itself suggested the scoped name. Inside your scope you still need a name you have not used before.

Should my PyPI name match the name people import?

Usually, but nothing forces it: "PyPI and other package indices do not enforce any relationship between the name of a distribution package and the import packages it provides" (Python Packaging User Guide). A match saves your users a lookup.

Should I add "js" or "node" to an npm name to make it unique?

npm's package.json docs say not to: "Don't put "js" or "node" in the name" (npm Docs). npm's 2017 post shows jsonstream blocking json.stream and json_stream, and the fit-map refusal above shows a near-variant blocked: adding a word to a taken name is where refusals happen.

Can I delete a crate and keep the name for later?

No. The Cargo Book says that once a crate name is taken, "it cannot be used for another crate", and deletion is the exception: owners may delete a crate only under set conditions, such as within 72 hours of publishing (crates.io policies), and the crates.io code then opens the name to anyone 24 hours later (crates.io source, delete.rs).

Sources

All pages read on 24 October 2026.