{"record":{"id":"57df9c3ca6362c3f","repo":"can1357/oh-my-pi","slug":"truncated-embedded-addon-archive-entry-filename","errorCode":null,"errorMessage":"Truncated embedded addon archive entry: ${filename}","messagePattern":"Truncated embedded addon archive entry: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/natives/native/loader-state.js","lineNumber":523,"sourceCode":"\t\t\tpending.set(file.filename, file);\n\t\t}\n\t}\n\tif (pending.size === 0) return [];\n\n\tconst archive = zlib.gunzipSync(fs.readFileSync(archivePath));\n\tconst writtenPaths = [];\n\tlet offset = 0;\n\n\twhile (offset + 512 <= archive.length) {\n\t\tif (isZeroTarBlock(archive, offset)) break;\n\t\tconst header = archive.subarray(offset, offset + 512);\n\t\tconst filename = getTarEntryName(header);\n\t\tconst size = readTarOctal(header, 124, 12);\n\t\tconst typeflag = header[156] === 0 ? \"0\" : String.fromCharCode(header[156]);\n\t\toffset += 512;\n\n\t\tif (offset + size > archive.length) {\n\t\t\tthrow new Error(`Truncated embedded addon archive entry: ${filename}`);\n\t\t}\n\n\t\tif (!isSafeEmbeddedAddonFilename(filename)) {\n\t\t\tthrow new Error(`Unsafe embedded addon archive entry: ${filename}`);\n\t\t}\n\t\tif (typeflag !== \"0\") {\n\t\t\tthrow new Error(`Unsupported embedded addon archive entry type ${typeflag}: ${filename}`);\n\t\t}\n\n\t\tconst file = pending.get(filename);\n\t\tif (file) {\n\t\t\tif (typeof file.size === \"number\" && file.size !== size) {\n\t\t\t\tthrow new Error(`Embedded addon size mismatch for ${filename}: expected ${file.size}, got ${size}`);\n\t\t\t}\n\t\t\tconst targetPath = path.join(targetDir, filename);\n\t\t\twriteEmbeddedAddonFile(targetPath, archive.subarray(offset, offset + size));\n\t\t\tpending.delete(filename);\n\t\t\twrittenPaths.push(targetPath);","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/natives/native/loader-state.js#L505-L541","documentation":"While walking the gunzipped tar stream of the embedded addon archive, the loader reads each 512-byte header and its octal size at offset 124. If offset + size exceeds the archive buffer length, the entry's payload is not fully present and the loader throws instead of writing a truncated .node file.","triggerScenarios":"Calling extractEmbeddedAddonArchive where the .tar.gz decoded to fewer bytes than the tar headers claim: the last (or a middle) entry's declared size runs past the end of the decompressed buffer. Raised during the header walk, before any per-entry validation or write.","commonSituations":"A truncated or partially downloaded/corrupted archive file embedded in the binary; a build step that produced an incomplete .tar.gz; disk corruption; someone concatenating or editing the archive bytes.","solutions":["Reinstall omp / rebuild the binary so the embedded archive is regenerated intact.","Verify the archive file: gunzip -t then tar -tzf to confirm it is complete and well-formed.","Check the build pipeline that produces the .tar.gz asset for truncation (e.g. interrupted copy, size mismatch in packaging).","As a caller, delete any cached extracted files and retry extraction from a known-good binary."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"import * as zlib from \"node:zlib\";\nimport * as fs from \"node:fs\";\nfunction validateArchiveIntegrity(archivePath) {\n  const tar = zlib.gunzipSync(fs.readFileSync(archivePath));\n  let offset = 0;\n  while (offset + 512 <= tar.length && !isZeroBlock(tar, offset)) {\n    const size = parseInt(tar.toString(\"utf8\", offset + 124, offset + 136).replace(/\\0.*$/, \"\").trim(), 16) || 0;\n    offset += 512 + Math.ceil(size / 512) * 512;\n  }\n  return offset <= tar.length; // false => truncated archive\n}","typeGuard":null,"tryCatchPattern":"try {\n  extractEmbeddedAddonArchive({ archivePath, files, targetDir });\n} catch (err) {\n  if (String(err.message).startsWith(\"Truncated embedded addon archive entry:\")) {\n    // fall back to on-disk node_modules copy or trigger reinstall\n  } else throw err;\n}","preventionTips":["Verify archive checksums/size after download or build before embedding.","Use tar -tzf in CI to assert the archive lists every expected file completely.","Avoid interrupted copies: write to a temp file and rename atomically when producing the .tar.gz."],"tags":["archive","corruption","tar","truncated-data"],"backgroundTag":"truncated-archive","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}