{"record":{"id":"281dc1fbbff5dfce","repo":"nodejs/node","slug":"could-not-read-package-json-from-tarball","errorCode":null,"errorMessage":"Could not read package.json from tarball","messagePattern":"Could not read package\\.json from tarball","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"deps/npm/lib/commands/stage/download.js","lineNumber":64,"sourceCode":"  async #readManifestFromTarball (tarballData) {\n    let manifestJson\n    const stream = tar.t({\n      onentry (entry) {\n        if (entry.path === 'package/package.json') {\n          const chunks = []\n          entry.on('data', c => chunks.push(c))\n          entry.on('end', () => {\n            manifestJson = JSON.parse(Buffer.concat(chunks).toString())\n          })\n        } else {\n          entry.resume()\n        }\n      },\n    })\n    // node-tar uses Minipass which processes synchronously on .end()\n    stream.end(tarballData)\n    if (!manifestJson) {\n      throw new Error('Could not read package.json from tarball')\n    }\n    return manifestJson\n  }\n}\n\nmodule.exports = StageDownload\n","sourceCodeStart":46,"sourceCodeEnd":71,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/deps/npm/lib/commands/stage/download.js#L46-L71","documentation":"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.","triggerScenarios":"`#readManifestFromTarball` iterates the tar entries and finds none whose `entry.path === 'package/package.json'`; after synchronous `stream.end(tarballData)`, `manifestJson` is still undefined.","commonSituations":"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.","solutions":["Verify the staged tarball URL/ID is correct and the registry actually serves a valid package tarball.","Download the artifact manually and inspect it: `tar -tzf file.tgz | head` should list `package/package.json`.","Check registry/proxy responses (auth, redirect, error body) that may substitute non-tarball content.","Re-stage or re-publish the package so the tarball is well-formed."],"exampleFix":"// before: tarball missing package/package.json\n// after: ensure tarball layout is package/<files>\ntar -tzf pkg.tgz   // must show package/package.json","handlingStrategy":"validation","validationCode":"const tar = require('tar')\nasync function assertTarballHasManifest(buffer) {\n  let found = false\n  await tar.t({ file: /* path or */ null, onentry(e){ if (e.path === 'package/package.json') found = true } })\n  // for a Buffer use: const s = tar.t({onentry...}); s.end(buffer); await once(s,'end')\n  if (!found) throw new Error('Tarball has no package/package.json entry')\n  return true\n}","typeGuard":null,"tryCatchPattern":"try {\n  await stageDownload(stageId)\n} catch (e) {\n  if (/Could not read package\\.json from tarball/i.test(e.message)) {\n    // re-fetch / verify registry response body, then retry once\n  }\n  throw e\n}","preventionTips":["Validate tarballs with `tar -tzf file.tgz | grep package/package.json` before staging.","Log the registry response content-type/size to catch HTML error pages masquerading as tarballs.","Pin and verify registry/proxy configuration for staging workflows."],"tags":["stage","tarball","package-json","npm-cli","registry"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}