paperclipai/paperclip · error · Error
Unexpected bundled dependency.
Error message
Unexpected bundled dependency.
What it means
Bundled (inBundle) dependencies are only allowed under node_modules/@paperclipai/db/node_modules/. This error fires when the lockfile marks some other package as bundled — a package whose bytes ship inside another tarball rather than resolving from a registry — outside the one permitted location. Bundled deps elsewhere cannot be integrity-pinned consistently.
Solutions
- Remove the bundledDependencies declaration (or move it so only db may bundle), then rebuild the lockfile
- Re-run `node scripts/cloud-migrator-artifacts.mjs build <dir> <sha>` after changing the bundling config
- Inspect the entry's key to find which package introduced the bundle and fix at the source package.json
Example fix
// before (shared package.json)
"bundleDependencies": ["some-dep"]
// after (removed; dep resolves from registry with integrity pin)
"dependencies": { "some-dep": "^1.0.0" } Defensive patterns
Strategy: validation
Validate before calling
for (const [key, entry] of Object.entries(lock.packages ?? {})) {
if (entry?.inBundle === true && !key.startsWith("node_modules/@paperclipai/db/node_modules/"))
throw new Error(`unexpected bundled dep: ${key}`);
} Type guard
const bundlesOnlyUnderDb = (lock) =>
Object.entries(lock?.packages ?? {}).every(([key, entry]) =>
entry?.inBundle !== true || key.startsWith("node_modules/@paperclipai/db/node_modules/")); Try / catch
try {
assertLockfile(lock, manifest);
} catch (err) {
if (err.message === "Unexpected bundled dependency.") throw new Error("Remove bundledDependencies outside @paperclipai/db and rebuild");
throw err;
} Prevention
- Do not declare bundledDependencies in @paperclipai/shared or the install root
- Rebuild the lockfile after changing any packing/bundling configuration
- Inspect entry.inBundle flags in the lockfile before publishing
- Keep dependency resolution registry-based (bundling bypasses integrity pins)
When it happens
Trigger: assertLockfile iterates lock.packages and finds an entry with inBundle === true whose key does not start with node_modules/@paperclipai/db/node_modules/ (e.g. shared bundling a dep, or a root-level bundled package).
Common situations: A package.json gained bundledDependencies/bundleDependencies listing a package; npm packed a dep into the shared tarball; lockfile generated from a different packing configuration than the build expects.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- Invalid migrator lockfile root.
- ACPX provider package name is invalid
- Invalid migrator lockfile entry.
- Migrator lockfile package pin mismatch.
- Unexpected internal migrator dependency.
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-18).
Data as JSON: /api/errors/51387d5ec480b1ff.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/cloud-migrator-artifacts.mjs:54
assertDescriptor(manifest.lockfile, "json");
}
export function assertLockfile(lock, manifest) {
const version = manifest.packageVersion;
if (lock?.lockfileVersion !== 3 || !lock.packages || Array.isArray(lock.packages) ||
JSON.stringify(lock.packages[""]?.dependencies) !== JSON.stringify({ "@paperclipai/db": version })) throw new Error("Invalid migrator lockfile root.");
for (const name of names) {
const pin = lock.packages[`node_modules/@paperclipai/${name}`];
const expected = manifest.packages[name];
if (pin?.version !== version || pin.integrity !== expected.integrity || pin.resolved !== expected.url || pin.link || pin.inBundle) throw new Error("Migrator lockfile package pin mismatch.");
}
if (lock.packages["node_modules/@paperclipai/db"].dependencies?.["@paperclipai/shared"] !== version) throw new Error("Migrator shared dependency mismatch.");
for (const [key, entry] of Object.entries(lock.packages)) {
if (key === "") continue;
if (!entry || typeof entry !== "object" || entry.link) throw new Error("Invalid migrator lockfile entry.");
if (/(?:^|\/)node_modules\/@paperclipai\/[^/]+$/.test(key) && !names.some((name) => key === `node_modules/@paperclipai/${name}`)) throw new Error("Unexpected internal migrator dependency.");
if (entry.inBundle === true) {
if (!key.startsWith("node_modules/@paperclipai/db/node_modules/")) throw new Error("Unexpected bundled dependency.");
continue;
}
if (!/^sha512-[A-Za-z0-9+/]{86}==$/.test(entry.integrity ?? "")) throw new Error("Migrator dependency has no strong integrity pin.");
if (names.some((name) => key === `node_modules/@paperclipai/${name}`)) continue;
const url = new URL(entry.resolved);
if (url.origin !== "https://registry.npmjs.org" || url.username || url.password || url.search || url.hash) throw new Error("Migrator dependency must resolve to npm.");
}
}
export function buildBundle(directory, sha, { exec = execFileSync } = {}) {
versionFor(sha);
directory = path.resolve(directory);
const packages = {};
for (const name of names) {
const bytes = readFileSync(path.join(directory, `${name}.tgz`));
assertMetadata(tarManifest(bytes), `@paperclipai/${name}`, sha);
packages[name] = descriptor(bytes, "tgz");
}View on GitHub (pinned to 3f1d897a7c)