pydantic/monty · error
failed to read
Error message
failed to read {}: {e} What it means
The pydantic-monty-client build script syncs the pinned dist versions in packages/pydantic-monty/pyproject.toml from the Cargo workspace version. It panics with this message when fs::read_to_string on the pyproject.toml fails — the file is missing, unreadable, or the path is wrong at build time.
Solutions
- Restore packages/pydantic-monty/pyproject.toml (git checkout -- packages/pydantic-monty/pyproject.toml or a fresh clone)
- Verify the file path/permissions: ls -l packages/pydantic-monty/pyproject.toml
- If the metapackage moved, update the path constant in crates/monty-python/build.rs
Example fix
// before $ cargo build -p pydantic-monty-client panicked: failed to read packages/pydantic-monty/pyproject.toml: No such file or directory // after git checkout -- packages/pydantic-monty/pyproject.toml cargo build -p pydantic-monty-client # succeeds
Defensive patterns
Strategy: validation
Validate before calling
test -f packages/pydantic-monty/pyproject.toml || echo 'metapackage pyproject.toml missing'
Prevention
- Keep the full workspace checked out (no partial/sparse copies) before building
- Do not delete or move packages/pydantic-monty without updating build.rs
- Check out with git clone (not selective file copies) in CI and containers
When it happens
Trigger: Running `cargo build`/`cargo publish` for crates/monty-python when packages/pydantic-monty/pyproject.toml does not exist, was deleted, moved, or is unreadable due to file permissions.
Common situations: Fresh checkout missing the metapackage directory; build run from a sparse/partial workspace; a refactor renamed packages/pydantic-monty; build executed in a container that copied only part of the repo.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
AI-assisted analysis of pydantic/monty@adc986b362 (2026-09-13).
Data as JSON: /api/errors/a848005945e6fb18.
Report an issue: GitHub.
Appendix: source
Thrown at crates/monty-python/build.rs:46
pyo3_build_config::use_pyo3_cfgs();
}
/// Rewrite the metapackage's `version` and its pins on this distribution and
/// `pydantic-monty-runtime` if any has drifted from the Cargo package version.
///
/// All three distributions are built from the same workspace and released
/// together — `pydantic-monty` exists only to install the other two, and the
/// worker rejects a client whose version differs — so the pins must be exact.
/// Unlike monty-js there is no lockfile to refresh afterwards: uv.lock records
/// no specifier for workspace-editable sources.
///
/// Uses the runtime `CARGO_PKG_VERSION` env var (not `env!()`) so that the build
/// script picks up version changes without needing to be recompiled.
fn sync_meta_pyproject(path: &Path) {
let cargo_version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION not set");
let pin_version = cargo_version_to_pep440(&cargo_version);
let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()));
let mut result = String::with_capacity(contents.len());
let mut changed = false;
// one count per entry of `PINNED_DISTS`, plus the `version = ` line
let mut pins = [0usize; PINNED_DISTS.len()];
let mut versions = 0usize;
for line in contents.lines() {
let synced = if let Some(index) = pinned_dist(line) {
pins[index] += 1;
// Preserve the presence/absence of the trailing comma (the last
// entry in the dependencies array has none).
let comma = if line.ends_with(',') { "," } else { "" };
Cow::Owned(format!(" \"{}=={pin_version}\"{comma}", PINNED_DISTS[index]))
} else if line.starts_with("version = \"") {
versions += 1;
Cow::Owned(format!("version = \"{pin_version}\""))
} else {View on GitHub (pinned to adc986b362)