perspective-dev/perspective · error · Error
labextension is not present in data directory, please build…
Error message
labextension is not present in data directory, please build `perspective-jupyterlab`
What it means
This build script for the perspective-python wheel verifies that the JupyterLab labextension artifact (@perspective-dev/jupyterlab package.json) was copied into the wheel data directory. It throws because the file is absent, meaning the perspective-jupyterlab frontend package was never built or its output was not staged into data_dir before this check at build.mjs:130.
Solutions
- Build the jupyterlab extension first: run the perspective-jupyterlab package's build (e.g. `pnpm build` in packages/perspective-jupyterlab or the monorepo-wide build) before building perspective-python
- Run the full monorepo build so all frontend artifacts are produced and staged, then rebuild the python wheel
- Verify the labextension output exists at packages path and that build.mjs's copy step into data_dir ran (check for errors earlier in the build log)
- If the artifact exists but is misplaced, ensure you run the build from the repository root as expected (relative path `data_dir/data/share/jupyter/...` depends on cwd)
Example fix
# before $ cd rust/perspective-python && node build.mjs Error: labextension is not present in data directory... // after $ pnpm install && pnpm build # builds all packages incl. perspective-jupyterlab $ cd rust/perspective-python && node build.mjs # proceeds past the check
Defensive patterns
Strategy: validation
Validate before calling
const labext = require('fs').existsSync('data/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/package.json');
if (!labext) throw new Error('Build perspective-jupyterlab before building perspective-python'); Try / catch
try { await buildPython(); } catch (e) { if (/labextension is not present/.test(e.message)) { console.error('Run `pnpm build` in perspective-jupyterlab first'); process.exit(1); } throw e; } Prevention
- Always run the full monorepo build before building the python wheel
- Add a pre-build step in CI that asserts labextension artifacts exist
- Keep frontend and python build steps ordered in your build pipeline
- Build from the repository root so relative paths resolve
When it happens
Trigger: Running `node build.mjs` (or the Python build/pip install that invokes it) when `data_dir/data/share/jupyter/labextensions/@perspective-dev/jupyterlab/package.json` does not exist — typically the perspective-jupyterlab package was not built first or its dist output was not copied into the data directory.
Common situations: Cloning the monorepo and building perspective-python without first building the jupyterlab extension; running a partial/clean build; CI caching that skips the frontend build; building only the python wheel target.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
AI-assisted analysis of perspective-dev/perspective@11c8238c0c (2026-09-09).
Data as JSON: /api/errors/adc694b6c484836b.
Report an issue: GitHub.
Appendix: source
Thrown at rust/perspective-python/build.mjs:130
if (build_sdist) {
// `maturin sdist` has some issues with Cargo workspaces, so we assemble the sdist by hand here
// Note that the resulting sdist is _not_ a Cargo workspace, it is rooted in this package.
const cargo_toml = fs.readFileSync("./Cargo.toml").toString("utf-8");
const pyproject_toml = fs
.readFileSync("./pyproject.toml")
.toString("utf-8");
const cargo = toml.parse(cargo_toml);
const pyproject = toml.parse(pyproject_toml);
const version = cargo["package"]["version"];
const data_dir = `perspective_python-${version}.data`;
const testfile = path.join(
data_dir,
"data/share/jupyter/labextensions/@perspective-dev/jupyterlab/package.json",
);
if (!fs.existsSync(testfile)) {
throw new Error(
"labextension is not present in data directory, please build `perspective-jupyterlab`",
);
}
if (
!fs.existsSync("perspective/widget/static/perspective-anywidget.js")
) {
throw new Error(
"anywidget bundle is not present in perspective/widget/static, please build `@perspective-dev/anywidget`",
);
}
const readme_md = fs.readFileSync("./README.md");
const pkg_info = generatePkgInfo(pyproject, cargo, readme_md);
fs.writeFileSync("./PKG-INFO", pkg_info);
// Maturin finds extra license files in the root of the source directory,
// then packages them into .dist-info in the wheel. As of Nov 2024,View on GitHub (pinned to 11c8238c0c)