k3s-io/k3s · error
only wrote %d bytes to %s; expected %d
Error message
only wrote %d bytes to %s; expected %d
What it means
io.Copy into the extracted file returned no error but wrote fewer bytes than the tar header declared (f.Size). The tar stream is therefore shorter than its header claims - usually a truncated download, a zstd stream cut mid-entry, or a corrupt archive - and the extractor refuses to leave a silently incomplete file on disk.
Source
Thrown at pkg/untar/untar.go:91
if !madeDir[dir] {
if err := os.MkdirAll(filepath.Dir(abs), 0755); err != nil {
return err
}
madeDir[dir] = true
}
wf, err := os.OpenFile(abs, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode.Perm())
if err != nil {
return err
}
n, err := io.Copy(wf, tr)
if closeErr := wf.Close(); closeErr != nil && err == nil {
err = closeErr
}
if err != nil {
return fmt.Errorf("error writing to %s: %v", abs, err)
}
if n != f.Size {
return fmt.Errorf("only wrote %d bytes to %s; expected %d", n, abs, f.Size)
}
modTime := f.ModTime
if modTime.After(t0) {
// Clamp modtimes at system time. See
// golang.org/issue/19062 when clock on
// buildlet was behind the gitmirror server
// doing the git-archive.
modTime = t0
}
if !modTime.IsZero() {
if err := os.Chtimes(abs, modTime, modTime); err != nil && !loggedChtimesError {
// benign error. Gerrit doesn't even set the
// modtime in these, and we don't end up relying
// on it anywhere (the gomote push command relies
// on digests only), so this is a little pointless
// for now.
logrus.Printf("error changing modtime: %v (further Chtimes errors suppressed)", err)
loggedChtimesError = true // once is enoughView on GitHub (pinned to 6ba341e396)
Solutions
- Verify integrity at the source: compare sha256sum with the producer, or run zstd -t file.tar.zst
- Re-download or regenerate the tarball and retry the extraction
- If you build the archive yourself, ensure tar.Close() and the zstd writer are flushed/closed before shipping
- Check producer-side disk space if the archive was created on a nearly full host
Defensive patterns
Strategy: validation
Validate before calling
func VerifyBundle(path, wantSHA256 string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close()
h := sha256.New()
if _, err := io.Copy(h, f); err != nil {
return err
}
if got := hex.EncodeToString(h.Sum(nil)); got != wantSHA256 {
return fmt.Errorf("checksum mismatch: got %s want %s", got, wantSHA256)
}
return nil
} Try / catch
if err := untar.Untar(r, dir); err != nil {
if strings.Contains(err.Error(), "only wrote") && strings.Contains(err.Error(), "expected") {
// truncated/corrupt archive: delete the partial dir and re-download
os.RemoveAll(dir)
}
return err
} Prevention
- Always verify sha256 of downloaded tarballs before extraction
- Use zstd -t (or gzip -t) to validate compressed streams after download
- Download to a temp file then extract; never stream-extract straight from a network connection
- Ensure producers close tar and zstd writers before publishing archives
When it happens
Trigger: untar.Untar on a tarball truncated mid-file-entry (interrupted download, proxy cut-off, partial copy); a zstd stream that decodes short; a header whose Size is larger than the data actually stored.
Common situations: Image/airgap bundles partially downloaded over flaky networks; artifacts truncated by CI artifact size caps or storage limits; producer wrote the archive on a full disk or did not close/flush the tar and zstd writers before shipping.
Related errors
- error writing to %s: %v
- tar contained invalid name error %q
- tar file entry %s contained unsupported file type %v
- invalid cipher text, not : delimited
- etcd member has status errors: %s
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/312ad46b22d26b20.
Report an issue: GitHub.