{"record":{"id":"d96db63282b202bf","repo":"paperclipai/paperclip","slug":"acpx-provider-package-root-must-be-an-explicit-nor","errorCode":null,"errorMessage":"ACPX provider package root must be an explicit normalized absolute path","messagePattern":"ACPX provider package root must be an explicit normalized absolute path","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts","lineNumber":295,"sourceCode":"const providerExitProof = new WeakMap<ChildProcess, Promise<void>>();\n\nexport type AcpxPackageJsonResolver = (\n  packageName: string,\n  issuerPackageJsonPath?: string,\n) => string;\n\nexport function createAcpxPackageJsonResolver(\n  providerPackageRoot: string | undefined,\n  providerPackageManifest?: string,\n): AcpxPackageJsonResolver {\n  const root = providerPackageRoot?.trim();\n  if (\n    !root ||\n    !isAbsolute(root) ||\n    root.includes(\"\\0\") ||\n    resolve(root) !== root\n  ) {\n    throw new Error(\n      \"ACPX provider package root must be an explicit normalized absolute path\",\n    );\n  }\n  const manifest = (\n    providerPackageManifest ?? resolve(root, \"package.json\")\n  ).trim();\n  if (\n    !manifest ||\n    !isAbsolute(manifest) ||\n    manifest.includes(\"\\0\") ||\n    resolve(manifest) !== manifest\n  ) {\n    throw new Error(\n      \"ACPX provider package manifest must be an explicit normalized absolute path\",\n    );\n  }\n  const canonicalRoot = realpathSync(root);\n  const canonicalManifest = realpathSync(manifest);","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts#L277-L313","documentation":"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.","triggerScenarios":"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'.","commonSituations":"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).","solutions":["Wrap the configured root with path.resolve(path.normalize(root)) before calling the resolver","Expand '~' and environment variables in the config value before use","Reject empty config values upstream with a clear 'provider root not configured' message instead of letting the resolver throw","Verify with isAbsolute(root) && resolve(root) === root in your own code and log the offending value"],"exampleFix":"// before\nconst resolver = createAcpxPackageJsonResolver(cfg.providerRoot);\n// after\nconst root = path.resolve(expandTilde(cfg.providerRoot));\nif (!path.isAbsolute(root) || path.resolve(root) !== root) throw new Error('providerRoot must be a normalized absolute path');\nconst resolver = createAcpxPackageJsonResolver(root);","handlingStrategy":"validation","validationCode":"import path from 'node:path';\nfunction assertValidRoot(root: string | undefined): asserts root is string {\n  if (!root || !path.isAbsolute(root) || root.includes('\\0') || path.resolve(root) !== root) {\n    throw new Error(`providerRoot must be a normalized absolute path, got: ${JSON.stringify(root)}`);\n  }\n}","typeGuard":"function isNormalizedAbsolutePath(p: unknown): p is string {\n  return typeof p === 'string' && p.length > 0 && path.isAbsolute(p) && !p.includes('\\0') && path.resolve(p) === p;\n}","tryCatchPattern":"try {\n  const resolver = createAcpxPackageJsonResolver(cfg.providerRoot);\n} catch (err) {\n  if (err instanceof Error && /explicit normalized absolute path/.test(err.message)) {\n    throw new Error(`Invalid providerRoot in config: ${JSON.stringify(cfg.providerRoot)} — use path.resolve()`);\n  }\n  throw err;\n}","preventionTips":["Always run configured paths through path.resolve() at config-load time, not at use time","Expand '~' and env vars before validation","Store absolute paths in config files rather than relative ones","Add a config-schema check (e.g. zod refine()) mirroring the resolver's path invariants"],"tags":["acpx","path-validation","configuration","node"],"backgroundTag":"invalid-argument-value","analyzedSha":"01ad8584922b5d85292b1723cae71fa0d9b07a19","analyzedAt":"2026-09-10T03:14:50.855Z","contentChangedAt":"2026-09-10T03:14:50.855Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}