{"record":{"id":"e45deefdddae1e5e","repo":"pnpm/pnpm","slug":"hooks-readpackage-should-be-a-function","errorCode":null,"errorMessage":"hooks.readPackage should be a function","messagePattern":"hooks\\.readPackage should be a function","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"pnpm11/hooks/pnpmfile/src/requirePnpmfile.ts","lineNumber":65,"sourceCode":"  try {\n    let pnpmfile: Pnpmfile\n    // Check if it's an ESM module (ends with .mjs)\n    if (pnpmFilePath.endsWith('.mjs')) {\n      const url = pathToFileURL(path.resolve(pnpmFilePath)).href\n      pnpmfile = await import(url)\n    } else {\n      // Use require for CommonJS modules\n      pnpmfile = require(pnpmFilePath)\n    }\n    if (typeof pnpmfile === 'undefined') {\n      logger.warn({\n        message: `Ignoring the pnpmfile at \"${pnpmFilePath}\". It exports \"undefined\".`,\n        prefix,\n      })\n      return { pnpmfileModule: undefined }\n    }\n    if (pnpmfile?.hooks?.readPackage && typeof pnpmfile.hooks.readPackage !== 'function') {\n      throw new TypeError('hooks.readPackage should be a function')\n    }\n    if (pnpmfile?.hooks?.readPackage) {\n      const readPackage = pnpmfile.hooks.readPackage as Function // eslint-disable-line\n      pnpmfile.hooks.readPackage = async function (pkg: PackageManifest, ...args: any[]) { // eslint-disable-line\n        pkg.dependencies = pkg.dependencies ?? {}\n        pkg.devDependencies = pkg.devDependencies ?? {}\n        pkg.optionalDependencies = pkg.optionalDependencies ?? {}\n        pkg.peerDependencies = pkg.peerDependencies ?? {}\n        const newPkg = await readPackage(pkg, ...args)\n        if (!newPkg) {\n          throw new BadReadPackageHookError(pnpmFilePath, 'readPackage hook did not return a package manifest object.')\n        }\n        const dependencies = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const\n        for (const dep of dependencies) {\n          if (newPkg[dep] != null && (typeof newPkg[dep] !== 'object' || Array.isArray(newPkg[dep]))) {\n            throw new BadReadPackageHookError(pnpmFilePath, `readPackage hook returned package manifest object's property '${dep}' must be an object.`)\n          }\n        }","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/pnpm/pnpm/blob/5b11d3a15b9022a2109cb18ed96a5d652630371f/pnpm11/hooks/pnpmfile/src/requirePnpmfile.ts#L47-L83","documentation":"requirePnpmfile validates the shape of every loaded pnpmfile: if `hooks.readPackage` is present but not a function, it throws a plain TypeError 'hooks.readPackage should be a function'. This is a pnpmfile contract violation (programmer error), not a PnpmError, so it carries no ERR_PNPM_* code.","triggerScenarios":"A pnpmfile exports `hooks: { readPackage: {...} }` (an object instead of a function), a string, or a mis-shaped default export; exporting a config object intended for another tool under hooks.","commonSituations":"Pnpmfile written against a different hooks API shape; copy-paste placing the function under the wrong key; a build step generating the pnpmfile with wrong output.","solutions":["Export readPackage as a real function: `module.exports = { hooks: { readPackage: (pkg) => pkg } }`","Check sibling validations in the same loader: hooks.beforePacking must also be a function if present","If the hook is not needed, remove the readPackage key entirely instead of leaving a non-function value"],"exampleFix":"// before\nmodule.exports = { hooks: { readPackage: { transform: true } } }\n// after\nmodule.exports = { hooks: { readPackage: (pkg) => pkg } }","handlingStrategy":"type-guard","validationCode":"const mod = require(pnpmfilePath)\nif (mod?.hooks?.readPackage != null && typeof mod.hooks.readPackage !== 'function') {\n  throw new TypeError(`pnpmfile ${pnpmfilePath}: hooks.readPackage must be a function`)\n}","typeGuard":"function hasValidReadPackage (pnpmfile: unknown): boolean {\n  const hooks = (pnpmfile as { hooks?: Record<string, unknown> })?.hooks\n  const rp = hooks?.readPackage\n  return rp == null || typeof rp === 'function'\n}","tryCatchPattern":"try {\n  await requirePnpmfile(file, prefix)\n} catch (err: unknown) {\n  if (util.types.isNativeError(err) && err.message === 'hooks.readPackage should be a function') {\n    // the pnpmfile exports readPackage as a non-function: fix its shape\n  }\n  throw err\n}","preventionTips":["Type pnpmfiles in TypeScript using the published hooks interface so shape errors surface at compile time","Keep hooks as a plain object of functions; do not put configuration under hooks","Validate a new or generated pnpmfile with a require + typeof check in a test"],"tags":["pnpmfile","hooks","read-package","typeerror","validation"],"backgroundTag":null,"analyzedSha":"5b11d3a15b9022a2109cb18ed96a5d652630371f","analyzedAt":"2026-08-16T13:18:59.198Z","schemaVersion":2},"datasetVersion":"2026-08-16T18:17:16.020Z"}