docker/compose · error

writing %q header: %w

Error message

writing %q header: %w

What it means

For non-regular files (directories, symlinks, fifos), writeEntry only writes a tar header. If tar.Writer.WriteHeader fails, the entry path is reported with 'writing %q header'. Typical cause: the underlying pipe writer errored (consumer side closed/failed) so all subsequent header writes fail, or the header itself is invalid (bad name, unsupported typeflag after go-archive filtering).

Source

Thrown at internal/sync/tar.go:180

	entries = dedupeEntries(entries)
	for _, entry := range entries {
		err := a.writeEntry(entry)
		if err != nil {
			return fmt.Errorf("archiving %q: %w", entry.path, err)
		}
	}
	return nil
}

func (a *ArchiveBuilder) writeEntry(entry archiveEntry) error {
	pathInTar := entry.path
	header := entry.header

	if header.Typeflag != tar.TypeReg {
		// anything other than a regular file (e.g. dir, symlink) just needs the header
		if err := a.tw.WriteHeader(header); err != nil {
			return fmt.Errorf("writing %q header: %w", pathInTar, err)
		}
		return nil
	}

	file, err := os.Open(pathInTar)
	if err != nil {
		// In case the file has been deleted since we last looked at it.
		if os.IsNotExist(err) {
			return nil
		}
		return err
	}

	defer func() {
		_ = file.Close()
	}()

	// The size header must match the number of contents bytes.

View on GitHub (pinned to ddc4b044b6)

Solutions

  1. Read the wrapped cause: if it is a pipe closed error, the real problem is on the Untar/container side — follow error 102's diagnosis for that container
  2. Ensure the destination container stays alive for the duration of the sync
  3. If the named entry is an odd file type (socket, device), exclude it from the sync mapping since it cannot be represented in tar
  4. Retry after the container-side issue is fixed; a poisoned writer never recovers within one archive build
Defensive patterns

Strategy: try-catch

Validate before calling

null // header failure is a downstream symptom of a poisoned writer; cannot be pre-validated cheaply

Try / catch

// if the wrapped error contains 'closed pipe', skip local fixes and diagnose the Untar consumer (container) instead
if strings.Contains(err.Error(), "pipe") { diagnoseContainer() } else { checkEntry(path) }

Prevention

When it happens

Trigger: a.tw.WriteHeader(header) failing for a Typeflag != tar.TypeReg entry — usually because the io.Pipe reader (the Untar side) has closed with an error, or the archive/tar writer was already poisoned by a previous failed write.

Common situations: The container-side Untar died first (container stopped) so the pipe broke and every following header write in the builder goroutine fails with the wrapped pipe error; or an exotic file type produced an unusable header.

Related errors


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