nodejs/node · error · Error

Could not read package.json from tarball

Error message

Could not read package.json from tarball

What it means

Thrown by the staging download path when the fetched tarball's entries never yielded a `package/package.json` entry, leaving `manifestJson` null after `stream.end()`. Every npm tarball must contain `package/package.json` at its root; absence means the tarball is malformed or the registry returned unexpected content.

Source

Thrown at deps/npm/lib/commands/stage/download.js:64

  async #readManifestFromTarball (tarballData) {
    let manifestJson
    const stream = tar.t({
      onentry (entry) {
        if (entry.path === 'package/package.json') {
          const chunks = []
          entry.on('data', c => chunks.push(c))
          entry.on('end', () => {
            manifestJson = JSON.parse(Buffer.concat(chunks).toString())
          })
        } else {
          entry.resume()
        }
      },
    })
    // node-tar uses Minipass which processes synchronously on .end()
    stream.end(tarballData)
    if (!manifestJson) {
      throw new Error('Could not read package.json from tarball')
    }
    return manifestJson
  }
}

module.exports = StageDownload

View on GitHub (pinned to 1b2de5e052)

Solutions

  1. Verify the staged tarball URL/ID is correct and the registry actually serves a valid package tarball.
  2. Download the artifact manually and inspect it: `tar -tzf file.tgz | head` should list `package/package.json`.
  3. Check registry/proxy responses (auth, redirect, error body) that may substitute non-tarball content.
  4. Re-stage or re-publish the package so the tarball is well-formed.

Example fix

// before: tarball missing package/package.json
// after: ensure tarball layout is package/<files>
tar -tzf pkg.tgz   // must show package/package.json
Defensive patterns

Strategy: validation

Validate before calling

const tar = require('tar')
async function assertTarballHasManifest(buffer) {
  let found = false
  await tar.t({ file: /* path or */ null, onentry(e){ if (e.path === 'package/package.json') found = true } })
  // for a Buffer use: const s = tar.t({onentry...}); s.end(buffer); await once(s,'end')
  if (!found) throw new Error('Tarball has no package/package.json entry')
  return true
}

Try / catch

try {
  await stageDownload(stageId)
} catch (e) {
  if (/Could not read package\.json from tarball/i.test(e.message)) {
    // re-fetch / verify registry response body, then retry once
  }
  throw e
}

Prevention

When it happens

Trigger: `#readManifestFromTarball` iterates the tar entries and finds none whose `entry.path === 'package/package.json'`; after synchronous `stream.end(tarballData)`, `manifestJson` is still undefined.

Common situations: Registry or proxy returned an HTML error page saved as a `.tgz`; a manually crafted tarball with a different top-level directory; a corrupted/partial download; a private registry misconfiguration returning JSON instead of a tarball.

Related errors


AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13). Data as JSON: /api/errors/281dc1fbbff5dfce. Report an issue: GitHub.