Repository navigation
attw broken by [email protected] - tarball extraction keeps only the empty final-flush chunk #262
Description
Summary
attw -P and attw --from-npm <anything> crash with Cannot read properties of undefined (reading 'filename') when [email protected] is hoisted into @arethetypeswrong/core's tree. 0.8.3 changed the streaming Gunzip to emit multiple callbacks where 0.8.2 emitted one; createPackageFromTarballData only keeps the last callback's chunk, which under 0.8.3 is always an empty final-flush chunk.
@arethetypeswrong/[email protected] declares "fflate": "^0.8.2", so a fresh install resolves to 0.8.3 and the tool is broken for any package, regardless of size.
Reproduction
One command - any published package works:
npx -p @arethetypeswrong/[email protected] attw --from-npm [email protected]
# → error while checking package:
# → Cannot read properties of undefined (reading 'filename')Confirmation that it's the fflate version
# in a project that has @arethetypeswrong/cli installed
npm i --no-save [email protected]
npx attw --from-npm [email protected] # ✅ works, prints the result table
npm i --no-save [email protected]
npx attw --from-npm [email protected] # ❌ crashesWhy it happens
packages/core/src/createPackage.ts (built form in dist/createPackage.js:228):
let unzipped;
new Gunzip((chunk) => (unzipped = chunk)).push(tarball, /*final*/ true);
const data = untar(unzipped);
const prefix = data[0].filename.substring(...); // throws when data is emptyInstrumented Gunzip callbacks against the [email protected] tarball:
| version | callbacks |
|---|---|
[email protected] |
[{ len: 10752, final: true }] |
[email protected] |
[{ len: 10752, final: false }, { len: 0, final: false }, { len: 0, final: true }] |
Because unzipped = chunk overwrites on each callback, unzipped ends up as the empty final chunk under 0.8.3. untar(empty) returns []. data[0] is undefined. .filename throws.
The comment in the source ("Use streaming API to work around 101arrowz/fflate#207") explains why gunzipSync was avoided, but the streaming code assumed exactly one callback - which 0.8.2 happened to provide.
Suggested fix
Accumulate chunks rather than overwriting, e.g.:
const chunks: Uint8Array[] = [];
new Gunzip((chunk) => chunks.push(chunk)).push(tarball, /*final*/ true);
let total = 0;
for (const c of chunks) total += c.length;
const unzipped = new Uint8Array(total);
let offset = 0;
for (const c of chunks) { unzipped.set(c, offset); offset += c.length; }
const data = untar(unzipped);Alternatively, revisit gunzipSync — fflate#207 may already be fixed, in which case gunzipSync(tarball) is simpler. A short-term option is to tighten the fflate dependency to ~0.8.2 until a real fix lands.
Environment
@arethetypeswrong/[email protected]@arethetypeswrong/[email protected][email protected](transitive, breaks);[email protected]works- Node 26.2.0, macOS 25.5.0
Activity
ericcornelissen commentedon May 23, 2026
Duplicate of #258(?)
ljharb commentedon May 24, 2026
yup, thanks
chore: remove attw script, add to roadmap