pydantic/monty · error
failed to write
Error message
failed to write {}: {e} What it means
After rewriting version pins in packages/pydantic-monty/pyproject.toml, the build script writes the updated contents back. This panic means the fs::write failed — typically a permissions problem, a read-only filesystem, or the target path being a directory. The version sync cannot complete, so the build aborts.
Solutions
- Check and fix file permissions: chmod u+w packages/pydantic-monty/pyproject.toml (or chown to your user)
- If the source tree is mounted read-only, copy the workspace to a writable location or run the build with the sources writable
- If you did not intend a version change, ensure CARGO_PKG_VERSION matches the pins already in pyproject.toml so no write is needed
Example fix
// before $ cargo build # after bumping version panicked: failed to write packages/pydantic-monty/pyproject.toml: Permission denied // after chmod u+w packages/pydantic-monty/pyproject.toml cargo build # version synced, succeeds
Defensive patterns
Strategy: try-catch
Validate before calling
[ -w packages/pydantic-monty/pyproject.toml ] && echo writable || echo 'not writable'
Prevention
- Bump versions only in a writable checkout
- Avoid read-only source mounts for builds; copy the tree to a writable dir
- Ensure the build user owns the source files
When it happens
Trigger: Cargo version changed (so `changed == true`) and the build script attempts to write packages/pydantic-monty/pyproject.toml while it is read-only, owned by another user, or lives on a read-only mount (e.g. packaged sources mounted ro in a container).
Common situations: Building inside a Docker container with the repo mounted read-only; bumping the workspace version locally when the file is owned by root; running a build as a non-privileged user in a shared checkout.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/7a64d753c49c2461.
Report an issue: GitHub.
Appendix: source
Thrown at crates/monty-python/build.rs:95
// which would otherwise leave the metapackage frozen at an old release.
for (dist, found) in PINNED_DISTS.iter().zip(pins) {
assert_eq!(
found,
1,
"expected exactly one `{dist}` pin in [project].dependencies of {}, found {found}",
path.display()
);
}
assert_eq!(
versions,
1,
"expected exactly one `version = \"...\"` line in {}, found {versions}",
path.display()
);
if changed {
eprintln!("Updating {} to {pin_version}", path.display());
fs::write(path, &result).unwrap_or_else(|e| panic!("failed to write {}: {e}", path.display()));
}
}
/// The PyPI distributions the metapackage pins exactly; see [`sync_meta_pyproject`].
const PINNED_DISTS: [&str; 2] = ["pydantic-monty-client", "pydantic-monty-runtime"];
/// The metapackage's pyproject.toml, which owns the `pydantic-monty` name on
/// PyPI, or `None` when there is nothing to sync.
///
/// The metapackage has no Cargo package of its own to carry the version, so
/// this build script — the one belonging to the distribution it pins —
/// maintains it. It lives outside this crate, and maturin's sdist ships only
/// the crate and its Rust path dependencies, so a source install legitimately
/// has no metapackage and must not panic over that.
fn meta_pyproject_path() -> Option<PathBuf> {
let path = PathBuf::from(env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set"))
.join("../../packages/pydantic-monty/pyproject.toml");
if path.is_file() {View on GitHub (pinned to adc986b362)