paperclipai/paperclip · error
ACPX provider package ${packageName} resolves outside the se
Error message
ACPX provider package ${packageName} resolves outside the selected provider root What it means
After resolving the requested package's package.json from the issuer, the resolver canonicalizes it with realpathSync and requires it to be inside the provider root's node_modules directory. This error is thrown when the package's manifest resolves elsewhere, meaning the dependency would be loaded from outside the verified provider installation. It is a fail-closed check against dependency confusion or hijacked resolution.
Source
Thrown at packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts:341
throw new Error(
"ACPX provider node_modules resolves outside the selected provider root",
);
}
return (packageName, issuerPackageJsonPath) => {
const canonicalIssuer =
issuerPackageJsonPath === undefined
? canonicalManifest
: realpathSync(issuerPackageJsonPath);
if (!pathIsInside(canonicalRoot, canonicalIssuer)) {
throw new Error(
`ACPX provider package issuer for ${packageName} resolves outside the selected provider root`,
);
}
const packageJsonPath = realpathSync(
resolvePackageJsonFromIssuer(packageName, canonicalIssuer),
);
if (!pathIsInside(canonicalNodeModules, packageJsonPath)) {
throw new Error(
`ACPX provider package ${packageName} resolves outside the selected provider root`,
);
}
return packageJsonPath;
};
}
function resolvePackageJsonFromIssuer(
packageName: string,
issuerPackageJsonPath: string,
): string {
const issuerRequire = createRequire(issuerPackageJsonPath);
try {
return issuerRequire.resolve(`${packageName}/package.json`);
} catch (error) {
if (
(error as NodeJS.ErrnoException).code !== "ERR_PACKAGE_PATH_NOT_EXPORTED"
)View on GitHub (pinned to 01ad858492)
Solutions
- Install the missing/misplaced package into the provider root's own node_modules (`cd <providerRoot> && npm/bun install`) so it resolves in-place.
- Compare `realpathSync(resolvedPath)` with `realpathSync(resolve(providerRoot,'node_modules'))`; if node_modules is a symlink, build the root so its realpath contains the dependency.
- Check for accidental global installs (`npm ls -g <pkg>`) and remove/alias them so local resolution wins.
- If the package is legitimately external, do not pass it through this resolver — it is only for packages belonging to the verified provider installation.
Example fix
// before
// dep hoisted outside provider root: /workspace/node_modules/@acpx/provider
const p = resolver("@acpx/provider", issuerManifest); // resolves outside
// after
cd <providerRoot> && bun install @acpx/provider # installs into providerRoot/node_modules
const p = resolver("@acpx/provider", issuerManifest); // inside canonicalNodeModules Defensive patterns
Strategy: validation
Validate before calling
const nmRoot = realpathSync(resolve(providerRoot, "node_modules"));
const resolved = createRequire(issuerManifest).resolve(`${pkg}/package.json`);
if (!isInside(nmRoot, realpathSync(resolved))) {
throw new Error(`${pkg} is not installed in the provider root's node_modules`);
} Type guard
function resolvesInsideNodeModules(providerRoot: string, issuer: string, pkg: string): boolean {
try {
const nm = realpathSync(resolve(providerRoot, "node_modules"));
const p = realpathSync(createRequire(issuer).resolve(`${pkg}/package.json`));
return isInside(nm, p);
} catch { return false; }
} Try / catch
try {
const manifestPath = resolver(pkg, issuer);
} catch (err) {
if (err instanceof Error && err.message.includes(pkg) && err.message.includes("outside the selected provider root")) {
// install the package into providerRoot/node_modules or fix symlink layout
} else throw err;
} Prevention
- Install all provider dependencies inside the provider root's own node_modules; never rely on hoisted ancestors or globals.
- After installs, spot-check with `node -e "console.log(require.resolve('<pkg>/package.json'))"` rooted at the provider.
- Keep dependency declarations pinned so resolution cannot silently move to a different tree.
- Watch for tooling (workspaces, bundlers) that redirects resolution outside the provider installation.
When it happens
Trigger: Calling the resolver for a package whose `require.resolve('pkg/package.json')` (via the issuer's require) resolves outside `<providerRoot>/node_modules` — e.g. hoisted deps above the root, a peer dependency satisfied from the host tree, or a symlink pointing out of the store.
Common situations: Package installed globally or in a parent workspace instead of the provider root's node_modules; pnpm virtual-store symlink whose realpath lands outside canonicalRoot (canonicalNodeModules itself is a symlink); version change where the package became a peer/optional dep resolved from an ancestor directory.
Understand the failure class
Background: Path traversal blocked: "path escapes the workspace" and "outside site root" errors when a path will not stay inside its allowed directory — this error's family across 26 libraries.
Related errors
- ACPX provider package issuer for ${packageName} resolves out
- Access denied
- Registered source Paperclip config escapes the base project
- Trusted viewer must not use symlinks
- ACPX provider package manifest resolves outside the selected
AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10).
Data as JSON: /api/errors/0c11e9ef62320e03.
Report an issue: GitHub.