paperclipai/paperclip · error
ACPX provider package issuer for
Error message
ACPX provider package issuer for ${packageName} resolves outside the selected provider root What it means
createAcpxPackageJsonResolver returns a resolver closure that, for each requested package, canonicalizes the issuer package.json path via realpathSync and verifies it lives inside the selected provider root. This error is thrown when the issuer manifest (explicitly passed, or defaulting to the provider root manifest) canonicalizes to a path outside canonicalRoot, typically via symlinks. It is a fail-closed containment check ensuring dependency resolution never anchors outside the verified provider installation.
Solutions
- Inspect the issuerPackageJsonPath with `realpathSync` and confirm it is inside the provider root passed to createAcpxPackageJsonResolver; if not, pass the correct issuer or correct provider root.
- If the issuer is a symlink into a store outside the root, either relocate the installation so symlinks stay inside the root, or pass the in-root manifest path explicitly instead of relying on the default.
- Recreate the provider root (clean install of the ACPX provider packages) so realpath of the manifest lands inside it.
- Verify the root argument is the canonical (realpath) root, not a path whose realpath differs (e.g. macOS /var vs /private/var).
Example fix
// before const resolver = createAcpxPackageJsonResolver(providerRoot); resolver(pkg, "/opt/other-store/pkg/package.json"); // outside providerRoot // after const issuer = resolve(providerRoot, "node_modules/pkg/package.json"); const resolver = createAcpxPackageJsonResolver(realpathSync(providerRoot)); resolver(pkg, issuer); // issuer realpath stays inside canonicalRoot
Defensive patterns
Strategy: validation
Validate before calling
import { realpathSync } from "node:fs";
import { relative, isAbsolute } from "node:path";
function isInside(root: string, candidate: string): boolean {
const r = relative(realpathSync(root), realpathSync(candidate));
return r !== "" && !r.startsWith("..") && !isAbsolute(r);
}
if (!isInside(providerRoot, issuerPath)) throw new Error("issuer outside provider root"); Type guard
function issuerInsideRoot(providerRoot: string, issuerPath?: string): boolean {
if (issuerPath === undefined) return true;
try { return isInside(providerRoot, issuerPath); } catch { return false; }
} Try / catch
try {
const manifestPath = resolver(packageName, issuerPath);
} catch (err) {
if (err instanceof Error && err.message.includes("issuer") && err.message.includes("outside the selected provider root")) {
// re-anchor issuer inside provider root or rebuild provider installation
} else throw err;
} Prevention
- Always pass issuer manifests that live inside the provider root; prefer the default (root manifest) when unsure.
- Canonicalize both root and issuer with realpathSync before validating containment yourself.
- Avoid installations that symlink package dirs to a store outside the root (e.g. shared pnpm stores).
- Log realpathSync(issuer) alongside the configured root when wiring profiles to catch drift early.
When it happens
Trigger: Calling the resolver (or defaultPackageJsonResolver) with an issuerPackageJsonPath that is a symlink into another location, points to a package outside providerPackageRoot, or refers to a manifest that was moved/relinked after createAcpxPackageJsonResolver computed canonicalRoot.
Common situations: Global/pnpm-style symlinked node_modules where package dirs are symlinked into a virtual store outside the provider root; a stale or hand-built provider root where the issuer manifest is a relative path resolved wrongly; passing a workspace package.json from outside the ACPX provider installation as the issuer.
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 runtime executable must be a real regular file
- ACPX provider package
- paperclip_runner_attachment_staging_path_denied
- Trusted viewer must not use symlinks
- A trusted viewer build is required for public chat reports
AI-assisted analysis of paperclipai/paperclip@3f1d897a7c (2026-09-10).
Data as JSON: /api/errors/a9f72a4f09d61c25.
Report an issue: GitHub.
Appendix: source
Thrown at packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts:338
throw new Error(
"ACPX provider package manifest resolves outside the selected provider root",
);
}
const canonicalNodeModules = realpathSync(
resolve(canonicalRoot, "node_modules"),
);
if (!pathIsInside(canonicalRoot, canonicalNodeModules)) {
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,View on GitHub (pinned to 3f1d897a7c)