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

  1. Install the missing/misplaced package into the provider root's own node_modules (`cd <providerRoot> && npm/bun install`) so it resolves in-place.
  2. Compare `realpathSync(resolvedPath)` with `realpathSync(resolve(providerRoot,'node_modules'))`; if node_modules is a symlink, build the root so its realpath contains the dependency.
  3. Check for accidental global installs (`npm ls -g <pkg>`) and remove/alias them so local resolution wins.
  4. 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

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


AI-assisted analysis of paperclipai/paperclip@01ad858492 (2026-09-10). Data as JSON: /api/errors/0c11e9ef62320e03. Report an issue: GitHub.