pnpm/pnpm · error · TypeError

hooks.readPackage should be a function

Error message

hooks.readPackage should be a function

What it means

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.

Source

Thrown at pnpm11/hooks/pnpmfile/src/requirePnpmfile.ts:65

  try {
    let pnpmfile: Pnpmfile
    // Check if it's an ESM module (ends with .mjs)
    if (pnpmFilePath.endsWith('.mjs')) {
      const url = pathToFileURL(path.resolve(pnpmFilePath)).href
      pnpmfile = await import(url)
    } else {
      // Use require for CommonJS modules
      pnpmfile = require(pnpmFilePath)
    }
    if (typeof pnpmfile === 'undefined') {
      logger.warn({
        message: `Ignoring the pnpmfile at "${pnpmFilePath}". It exports "undefined".`,
        prefix,
      })
      return { pnpmfileModule: undefined }
    }
    if (pnpmfile?.hooks?.readPackage && typeof pnpmfile.hooks.readPackage !== 'function') {
      throw new TypeError('hooks.readPackage should be a function')
    }
    if (pnpmfile?.hooks?.readPackage) {
      const readPackage = pnpmfile.hooks.readPackage as Function // eslint-disable-line
      pnpmfile.hooks.readPackage = async function (pkg: PackageManifest, ...args: any[]) { // eslint-disable-line
        pkg.dependencies = pkg.dependencies ?? {}
        pkg.devDependencies = pkg.devDependencies ?? {}
        pkg.optionalDependencies = pkg.optionalDependencies ?? {}
        pkg.peerDependencies = pkg.peerDependencies ?? {}
        const newPkg = await readPackage(pkg, ...args)
        if (!newPkg) {
          throw new BadReadPackageHookError(pnpmFilePath, 'readPackage hook did not return a package manifest object.')
        }
        const dependencies = ['dependencies', 'devDependencies', 'optionalDependencies', 'peerDependencies'] as const
        for (const dep of dependencies) {
          if (newPkg[dep] != null && (typeof newPkg[dep] !== 'object' || Array.isArray(newPkg[dep]))) {
            throw new BadReadPackageHookError(pnpmFilePath, `readPackage hook returned package manifest object's property '${dep}' must be an object.`)
          }
        }

View on GitHub (pinned to 5b11d3a15b)

Solutions

  1. Export readPackage as a real function: `module.exports = { hooks: { readPackage: (pkg) => pkg } }`
  2. Check sibling validations in the same loader: hooks.beforePacking must also be a function if present
  3. If the hook is not needed, remove the readPackage key entirely instead of leaving a non-function value

Example fix

// before
module.exports = { hooks: { readPackage: { transform: true } } }
// after
module.exports = { hooks: { readPackage: (pkg) => pkg } }
Defensive patterns

Strategy: type-guard

Validate before calling

const mod = require(pnpmfilePath)
if (mod?.hooks?.readPackage != null && typeof mod.hooks.readPackage !== 'function') {
  throw new TypeError(`pnpmfile ${pnpmfilePath}: hooks.readPackage must be a function`)
}

Type guard

function hasValidReadPackage (pnpmfile: unknown): boolean {
  const hooks = (pnpmfile as { hooks?: Record<string, unknown> })?.hooks
  const rp = hooks?.readPackage
  return rp == null || typeof rp === 'function'
}

Try / catch

try {
  await requirePnpmfile(file, prefix)
} catch (err: unknown) {
  if (util.types.isNativeError(err) && err.message === 'hooks.readPackage should be a function') {
    // the pnpmfile exports readPackage as a non-function: fix its shape
  }
  throw err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of pnpm/pnpm@5b11d3a15b (2026-08-16). Data as JSON: /api/errors/e45deefdddae1e5e. Report an issue: GitHub.