{"record":{"id":"747629ef48180c62","repo":"jordansissel/fpm","slug":"invalid-tar-stream-eof-before-end-of-tar-record","errorCode":null,"errorMessage":"Invalid tar stream, eof before end-of-tar record","messagePattern":"Invalid tar stream, eof before end-of-tar record","errorType":"exception","errorClass":"StandardError","httpStatus":null,"severity":"error","filePath":"lib/fpm/package/apk.rb","lineNumber":195,"sourceCode":"    empty_records = 0\n\n    open(temporary_target_path, \"wb\") do |target_file|\n\n      # Scan to find the location of the two contiguous null records\n      open(target_path, \"rb\") do |file|\n\n        until(empty_records == 2)\n\n          header = file.read(TAR_CHUNK_SIZE)\n\n          # clear off ownership info\n          header = replace_ownership_headers(header, true)\n\n          typeflag = header[TAR_TYPEFLAG_OFFSET]\n          ascii_length = header[TAR_LENGTH_OFFSET_START..TAR_LENGTH_OFFSET_END]\n\n          if(file.eof?())\n            raise StandardError.new(\"Invalid tar stream, eof before end-of-tar record\")\n          end\n\n          if(typeflag == \"\\0\")\n            empty_records += 1\n            next\n          end\n\n          record_length = ascii_length.to_i(8)\n          record_length = determine_record_length(record_length)\n\n          target_file.write(header)\n          target_file.write(file.read(record_length))\n        end\n      end\n    end\n\n    FileUtils::mv(temporary_target_path, target_path)\n  end","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/jordansissel/fpm/blob/b6d77ba72a560b687723376a0e5115c3a92634ad/lib/fpm/package/apk.rb#L177-L213","documentation":"FPM::Package::Apk#input parses an Alpine .apk tar stream record by record and terminates only after two consecutive zero-filled records (the standard end-of-tar marker). Hitting EOF before that marker means the stream ended early: the file is truncated or not a valid apk tar archive, and the conversion aborts.","triggerScenarios":"Running 'fpm -s apk -t deb foo.apk' where foo.apk is a partially downloaded package, an HTML error page saved with a .apk extension, or has a corrupted gzip layer so reads run dry before the terminator.","commonSituations":"Interrupted downloads or flaky mirrors; curl/wget without -f saving a 404 page as the package; passing a repository index file instead of the actual package; disk-full truncation of cached artifacts.","solutions":["Validate the archive independently: tar tzf foo.apk >/dev/null — if tar errors too, the file is bad","Re-download from a reliable mirror and verify size/sha256 against the APKINDEX checksum","Confirm it is really an Alpine package: 'file foo.apk' should report gzip data, and the payload should list control.tar + data.tar members","If it is a locally built apk, rebuild with abuild to regenerate a complete archive"],"exampleFix":"# before\nfpm -s apk -t deb foo.apk   # truncated download => Invalid tar stream\n\n# after\ncurl -fLO https://dl-cdn.alpinelinux.org/path/foo.apk\ntar tzf foo.apk >/dev/null && fpm -s apk -t deb foo.apk","handlingStrategy":"validation","validationCode":"unless system('tar', 'tzf', 'pkg.apk', out: File::NULL, err: File::NULL)\n  abort 'apk archive is truncated/corrupt; re-download before converting'\nend","typeGuard":null,"tryCatchPattern":"begin\n  pkg = FPM::Package::Apk.new\n  pkg.input('pkg.apk')\nrescue StandardError => e\n  warn \"invalid apk stream (#{e.message}); verify with: tar tzf pkg.apk\"\n  raise\nend","preventionTips":["Verify downloads with checksums from APKINDEX before packaging","Always curl -f so HTTP error pages are not saved as .apk","Smoke-test any archive input with 'tar t' before handing it to fpm"],"tags":["fpm","apk","alpine","tar","corruption","download"],"backgroundTag":"corrupt-archive-stream","analyzedSha":"b6d77ba72a560b687723376a0e5115c3a92634ad","analyzedAt":"2026-08-21T16:39:02.570Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}