Troubleshooting
Common errors and confusing behaviors, in symptom -> cause -> fix form -- most of them are molt refusing to do something unsafe on purpose.
Many of the entries below are deliberate stops, not failures. molt would rather halt with a clear message than produce a release that PyPI's immutability makes unfixable. If you hit something not covered here, the Design decisions page explains the reasoning behind most of molt's refusals.
molt version refuses: dynamic = ["version"] detected
Symptom. molt version stops with an error naming a package whose pyproject.toml declares dynamic = ["version"] (typically a setuptools-scm or hatch-vcs project).
Cause. With a dynamic version, the version is not written in the manifest -- it is derived from a git tag at build time. There is nothing in the file for molt to bump, and silently doing nothing would leave you with a "released" package whose version never moved. That silent no-op is worse than an error.
Fix. Pick one:
- Give the package a static
[project].versionthat molt manages. This is the supported path -- molt owns the number, writes it to the manifest, and creates the git tag. - Exclude the package from molt's release plan if it is versioned entirely by its own tag-driven tooling and you do not want molt touching it.
molt refuses rather than guesses here on purpose; see Design decisions.
Error: both [tool.molt] and .molt/config.json are present
Symptom. Any molt command fails immediately with a message that configuration was found in two places.
Cause. molt reads config from [tool.molt] in pyproject.toml, with .molt/config.json as a fallback -- but never both. Merging two sources would require precedence rules, and every precedence rule is a subtle footgun.
Fix. Delete one. Keep [tool.molt] in pyproject.toml unless you have a specific reason to prefer standalone JSON. See The config file.
molt version exits 1 with "no changesets"
Symptom. molt version prints that there are no pending changesets and exits with code 1, writing nothing.
Cause. This is intended behavior, not a crash. A version run with no changesets is a no-op, and molt makes that detectable by exiting non-zero -- so a CI pipeline can tell "nothing to release" apart from "released successfully." (changesets adopted the same exit-1 behavior in v3, after the old silent exit-0 caused snapshot-mode footguns.)
Fix. Usually nothing is wrong -- there is simply nothing to release. If you expected a release:
- Confirm you actually added a changeset (
molt statuslists what is pending). - Check that a previous
molt versiondid not already consume and delete the changesets (it deletes them once applied). - In CI, treat exit 1 from
versionas "no release this run," not as a build failure.
Snapshot upload rejected by PyPI
Symptom. A snapshot publish returns a 400 from PyPI -- often about a local version segment, or simply a rejected filename.
Cause. Snapshots are not meant for PyPI. PyPI rejects local-version suffixes (+abc1234), and even a legal .devN snapshot would permanently burn a public version number, since PyPI versions are immutable and cannot be unpublished. High-frequency snapshot publishing to PyPI is unworkable by design.
Fix. Target a non-PyPI index for snapshots -- a local wheelhouse, TestPyPI, or a private index (devpi, Artifactory, CodeArtifact). This is molt's default; you only see this error if snapshots were explicitly pointed at PyPI. If you need to share a build, prefer building the artifact and handing out the exact install command over publishing to a public index. See Snapshot releases.
Error: prerelease kind is not PEP 440-legal
Symptom. molt version --pre next (or any custom tag) fails, saying the prerelease kind is invalid.
Cause. PEP 440 allows prereleases only from a fixed vocabulary: a (alpha), b (beta), rc (release candidate), and dev. Arbitrary tags like next or canary -- legal in npm/SemVer -- cannot be represented, and PyPI would reject or renormalize them.
Fix. Use one of the legal kinds:
See Prerelease mode and molt pre.
Comments and formatting in pyproject.toml
Symptom. Concern (or a diff review) that molt rewriting [project].version and internal dependency ranges will strip comments or reflow the whole file.
Cause. A naive read-modify-write through a plain TOML parser discards comments and reformats -- which would be an instant-uninstall bug for a Python tool, since pyproject.toml comments are load-bearing to their authors.
Fix. Nothing to do -- molt uses tomlkit on the write path specifically to round-trip your file, so comments, key order, and formatting are preserved and only the values molt changes are touched. (Round-tripping is not always byte-perfect in pathological cases; if you ever see an unexpected reflow, it is worth reporting.) The read-only paths use the faster stdlib tomllib; only the handful of files molt actually rewrites go through tomlkit.
A changeset targets a package but nothing happens to it
Symptom. A changeset names a package, but molt version does not release it -- and molt prints a warning about it.
Cause. The package is being skipped -- it is in the ignore list, marked non-publishable, or has no version to bump. changesets drops such a reference silently; molt warns instead, so the mismatch is visible rather than a mystery.
Fix. Either remove the package from the changeset, or stop skipping it (adjust ignore / publish settings) if you did intend to release it. The warning tells you which package and which reason.
A changeset name does not match a workspace member
Symptom. A changeset front-matter key like Acme_Core seems not to match the project named acme-core.
Cause. This is not actually a problem -- molt applies PEP 503 name normalization when matching changeset keys against workspace members, so Acme_Core, acme_core, and acme-core all resolve to the same package. A changeset carried over from another tool may use inconsistent casing; it still matches.
Fix. Nothing required. If you want the file to read cleanly, use the canonical normalized name (acme-core), but any equivalent spelling works.
Where to go next
- Design decisions -- why molt refuses rather than guesses.
- The config file -- config location and format.
- Snapshot releases and Prerelease mode -- the version-shape rules behind two of the errors above.
- Comparison with changesets -- how these behaviors differ from changesets.