paperclipai/paperclip · error

ACPX provider package root must be an explicit normalized ab

Error message

ACPX provider package root must be an explicit normalized absolute path

What it means

createAcpxPackageJsonResolver validates its root parameter before resolving provider package.json files. The root must be a non-empty, absolute, null-byte-free, already-normalized path (resolve(root) === root). The library refuses relative paths, non-normalized paths containing '..' or '.', or paths with embedded NUL characters so downstream realpath/canonicalization logic cannot be tricked or fail ambiguously.

Source

Thrown at packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts:295

const providerExitProof = new WeakMap<ChildProcess, Promise<void>>();

export type AcpxPackageJsonResolver = (
  packageName: string,
  issuerPackageJsonPath?: string,
) => string;

export function createAcpxPackageJsonResolver(
  providerPackageRoot: string | undefined,
  providerPackageManifest?: string,
): AcpxPackageJsonResolver {
  const root = providerPackageRoot?.trim();
  if (
    !root ||
    !isAbsolute(root) ||
    root.includes("\0") ||
    resolve(root) !== root
  ) {
    throw new Error(
      "ACPX provider package root must be an explicit normalized absolute path",
    );
  }
  const manifest = (
    providerPackageManifest ?? resolve(root, "package.json")
  ).trim();
  if (
    !manifest ||
    !isAbsolute(manifest) ||
    manifest.includes("\0") ||
    resolve(manifest) !== manifest
  ) {
    throw new Error(
      "ACPX provider package manifest must be an explicit normalized absolute path",
    );
  }
  const canonicalRoot = realpathSync(root);
  const canonicalManifest = realpathSync(manifest);

View on GitHub (pinned to 01ad858492)

Solutions

  1. Wrap the configured root with path.resolve(path.normalize(root)) before calling the resolver
  2. Expand '~' and environment variables in the config value before use
  3. Reject empty config values upstream with a clear 'provider root not configured' message instead of letting the resolver throw
  4. Verify with isAbsolute(root) && resolve(root) === root in your own code and log the offending value

Example fix

// before
const resolver = createAcpxPackageJsonResolver(cfg.providerRoot);
// after
const root = path.resolve(expandTilde(cfg.providerRoot));
if (!path.isAbsolute(root) || path.resolve(root) !== root) throw new Error('providerRoot must be a normalized absolute path');
const resolver = createAcpxPackageJsonResolver(root);
Defensive patterns

Strategy: validation

Validate before calling

import path from 'node:path';
function assertValidRoot(root: string | undefined): asserts root is string {
  if (!root || !path.isAbsolute(root) || root.includes('\0') || path.resolve(root) !== root) {
    throw new Error(`providerRoot must be a normalized absolute path, got: ${JSON.stringify(root)}`);
  }
}

Type guard

function isNormalizedAbsolutePath(p: unknown): p is string {
  return typeof p === 'string' && p.length > 0 && path.isAbsolute(p) && !p.includes('\0') && path.resolve(p) === p;
}

Try / catch

try {
  const resolver = createAcpxPackageJsonResolver(cfg.providerRoot);
} catch (err) {
  if (err instanceof Error && /explicit normalized absolute path/.test(err.message)) {
    throw new Error(`Invalid providerRoot in config: ${JSON.stringify(cfg.providerRoot)} — use path.resolve()`);
  }
  throw err;
}

Prevention

When it happens

Trigger: Calling the resolver (directly or via defaultPackageJsonResolver) with root = '' , a relative path like './node/pkg', a non-normalized path like '/opt/acpx/../acpx/provider' where resolve(root) !== root, or a path containing '\0'.

Common situations: Config value for the provider package root read from an env var or settings file that was never path.resolve()'d; test code passing fixture-relative paths; a user config like '~/.acpx/provider' with an unexpanded tilde (not absolute).

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


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