docker/compose · error

finalizing

Error message

finalizing %q: %w

What it means

writeEntry explicitly calls tar.Writer.Flush after each entry so invalid entries are detected now rather than corrupting a later write. 'finalizing %q' means the flush of that entry's bytes to the underlying pipe failed or the writer detected an inconsistency (e.g. fewer bytes written than the header promised).

Solutions

  1. Treat the wrapped cause like errors 107/108: pipe-closed -> fix the container side; short-write -> stabilize the source file
  2. Retry the full sync after remediation; a flush failure invalidates the archive being streamed
  3. Keep the target container running and non-paused for the whole sync window
  4. Exclude volatile large files from hot sync paths or sync them at quiet moments
Defensive patterns

Strategy: try-catch

Validate before calling

null // flush outcome cannot be pre-validated

Try / catch

// 'finalizing %q' -> check consumer liveness and source stability, remediate, re-invoke Sync (archive is invalid after flush failure)

Prevention

When it happens

Trigger: a.tw.Flush() failing: the io.Pipe writer end returned an error (Untar consumer already closed), or the buffered writer detected a short write for the entry (size mismatch from a file modified mid-copy on the >5MB path).

Common situations: Consumer-side failure (container exited mid-extract) surfacing at flush time; large file truncated between stat and copy so the promised header size was never delivered.

Related errors


AI-assisted analysis of docker/compose@ddc4b044b6 (2026-08-15). Data as JSON: /api/errors/ca33c7118d356eaa. Report an issue: GitHub.

Appendix: source

Thrown at internal/sync/tar.go:242

	err = a.tw.WriteHeader(header)
	if err != nil {
		return fmt.Errorf("writing %q header: %w", pathInTar, err)
	}

	if useBuf {
		_, err = io.Copy(a.tw, a.copyBuf)
	} else {
		_, err = io.Copy(a.tw, file)
	}

	if err != nil && !errors.Is(err, io.EOF) {
		return fmt.Errorf("copying %q: %w", pathInTar, err)
	}

	// explicitly flush so that if the entry is invalid we will detect it now and
	// provide a more meaningful error
	if err := a.tw.Flush(); err != nil {
		return fmt.Errorf("finalizing %q: %w", pathInTar, err)
	}
	return nil
}

// entriesForPath writes the given source path into tarWriter at the given dest (recursively for directories).
// e.g. tarring my_dir --> dest d: d/file_a, d/file_b
// If source path does not exist, quietly skips it and returns no err
func (a *ArchiveBuilder) entriesForPath(localPath, containerPath string) ([]archiveEntry, error) {
	localInfo, err := os.Stat(localPath)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, err
	}

	localPathIsDir := localInfo.IsDir()
	if localPathIsDir {

View on GitHub (pinned to ddc4b044b6)