{"record":{"id":"2142cb431937ee53","repo":"github/copilot-sdk","slug":"failed-to-read-tar-w","errorCode":null,"errorMessage":"failed to read tar: %w","messagePattern":"failed to read tar: %w","errorType":"console","errorClass":null,"httpStatus":null,"severity":"error","filePath":"go/cmd/bundler/main.go","lineNumber":1104,"sourceCode":"\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to open release package: %w\", err)\n\t}\n\tdefer source.Close()\n\n\tgzReader, err := gzip.NewReader(source)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"failed to create gzip reader: %w\", err)\n\t}\n\tdefer gzReader.Close()\n\n\ttarReader := tar.NewReader(gzReader)\n\tfor {\n\t\theader, err := tarReader.Next()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to read tar: %w\", err)\n\t\t}\n\t\tswitch header.Name {\n\t\tcase \"package/LICENSE.md\", \"package/LICENSE\":\n\t\t\tlicenseName := filepath.Base(licensePath)\n\t\t\tif err := extractFileFromTarballStream(tarReader, outputDir, licenseName, os.FileMode(header.Mode)); err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to write license: %w\", err)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn fmt.Errorf(\"license file not found in tarball\")\n}\n\nfunc licensePathForOutput(outputPath string) string {\n\tif strings.HasSuffix(outputPath, \".zst\") {\n\t\treturn strings.TrimSuffix(outputPath, \".zst\") + \".license\"\n\t}","sourceCodeStart":1086,"sourceCodeEnd":1122,"githubUrl":"https://github.com/github/copilot-sdk/blob/cd8cf15dc3f9e762615790aaed0a771a0f392755/go/cmd/bundler/main.go#L1086-L1122","documentation":"extractCLILicense iterates the tar entries with tarReader.Next() to find package/LICENSE.md or package/LICENSE. Any error other than io.EOF while advancing the tar stream (i.e., malformed tar data after a valid gzip header) is wrapped as this error. It signals the decompressed archive is not a coherent tar stream.","triggerScenarios":"tarReader.Next() returns a non-EOF error mid-iteration — tar header corruption inside a nominally valid gzip stream, truncated archive, or data appended/corrupted after gzip layer.","commonSituations":"Partially downloaded tarball that still begins with a valid gzip header; storage bit-rot of a cached tarball; corrupted cache directory reused between builds.","solutions":["Delete the cached tarball and re-download; the checksum flow will validate the new copy.","Test archive integrity with tar -tzf <tarball> to localize the corruption.","Ensure nothing writes to the tarball after download (avoid shared cache paths).","Verify the release asset itself (upstream) is intact if fresh downloads also fail."],"exampleFix":"// before: reusing a cached tarball of unknown integrity\nlicenseExtracted := extractCLILicense(cachedTarball, outputDir, licensePath)\n\n// after: only trust tarballs whose checksum still matches\nif sha256File(cachedTarball) != expectedChecksum {\n    cachedTarball = reDownloadRelease()\n}\nextractCLILicense(cachedTarball, outputDir, licensePath)","handlingStrategy":"validation","validationCode":"// validate archive integrity before license extraction\nif err := verifySha256(tarballPath, expectedChecksum); err != nil {\n    tarballPath = reDownloadRelease(expectedChecksum)\n}","typeGuard":null,"tryCatchPattern":"if err := buildBundle(...); err != nil {\n    if strings.Contains(err.Error(), \"failed to read tar\") {\n        os.Remove(tarballPath); reDownload(); return retryBuild()\n    }\n    return err\n}","preventionTips":["Always validate SHA-256 before consuming cached tarballs.","Treat any non-EOF tar error as corruption; never continue past it.","Use immutable, per-build cache entries to prevent mid-stream mutation.","Test upstream release assets if fresh downloads keep failing."],"tags":["tar","corruption","extraction","go","license"],"backgroundTag":"checksum-mismatch","analyzedSha":"cd8cf15dc3f9e762615790aaed0a771a0f392755","analyzedAt":"2026-09-09T18:32:31.973Z","contentChangedAt":"2026-09-09T18:32:31.973Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}