{"record":{"id":"cb47a42e45f38b8c","repo":"paperclipai/paperclip","slug":"acpx-provider-package-manifest-must-be-an-explicit","errorCode":null,"errorMessage":"ACPX provider package manifest must be an explicit normalized absolute path","messagePattern":"ACPX provider package manifest 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":308,"sourceCode":"    !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);\n  if (!pathIsInside(canonicalRoot, canonicalManifest)) {\n    throw new Error(\n      \"ACPX provider package manifest resolves outside the selected provider root\",\n    );\n  }\n  const canonicalNodeModules = realpathSync(\n    resolve(canonicalRoot, \"node_modules\"),\n  );\n  if (!pathIsInside(canonicalRoot, canonicalNodeModules)) {\n    throw new Error(\n      \"ACPX provider node_modules resolves outside the selected provider root\",\n    );\n  }","sourceCodeStart":290,"sourceCodeEnd":326,"githubUrl":"https://github.com/paperclipai/paperclip/blob/01ad8584922b5d85292b1723cae71fa0d9b07a19/packages/paperclip-runner/src/drivers/acpx/installation-integrity.ts#L290-L326","documentation":"createAcpxPackageJsonResolver also validates the manifest path (the provider package.json location, whether supplied explicitly or defaulted from root). It must be non-empty, absolute, null-byte-free, and already normalized (resolve(manifest) === manifest). This runs before realpathSync so the check fires on the literal path rather than on whatever the path symlinks to.","triggerScenarios":"Passing providerPackageManifest as a relative path (e.g. 'package.json' or './pkg/package.json'), an empty string, a path containing '\\0', or a non-normalized path like '/opt/acpx/provider/../provider/package.json'.","commonSituations":"Constructing the manifest path by string concatenation without path.resolve; storing a relative manifest path in config; importing a provider spec file that records the manifest relative to its own directory.","solutions":["Convert the manifest to an absolute normalized path: path.resolve(root, 'package.json') before passing it in","If the manifest is configured, store it as an absolute path at config-load time","Sanitize/reject paths containing NUL bytes earlier, when reading user input","Add a pre-call assertion: isAbsolute(m) && resolve(m) === m && !m.includes('\\0')"],"exampleFix":"// before\nconst resolver = createAcpxPackageJsonResolver(root, providerManifestPath); // './pkg/package.json'\n// after\nconst manifest = path.resolve(root, providerManifestPath ?? 'package.json');\nconst resolver = createAcpxPackageJsonResolver(root, manifest);","handlingStrategy":"validation","validationCode":"import path from 'node:path';\nfunction normalizeManifest(root: string, manifest?: string): string {\n  const m = path.resolve(root, manifest ?? 'package.json');\n  if (m.includes('\\0')) throw new Error('manifest path contains NUL byte');\n  return m;\n}","typeGuard":"function isValidManifestPath(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(root, manifestPath);\n} catch (err) {\n  if (err instanceof Error && /manifest must be an explicit normalized absolute path/.test(err.message)) {\n    manifestPath = path.resolve(root, manifestPath ?? 'package.json');\n    return createAcpxPackageJsonResolver(root, manifestPath);\n  }\n  throw err;\n}","preventionTips":["Derive the manifest from the root with path.resolve(root, 'package.json') instead of hand-built strings","Never pass relative paths across module boundaries; normalize at the boundary","Reject NUL bytes when reading paths from user/network input","Add a test asserting all manifest-path fixtures are absolute and normalized"],"tags":["acpx","path-validation","configuration","node"],"backgroundTag":"invalid-argument-format","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"}