containerd/containerd · error

failed to get compressed stream: %w

Error message

failed to get compressed stream: %w

What it means

When the diff must be compressed, Compare first tries the user-supplied config.Compressor callback (if set) to obtain the compressed stream writer. If that callback returns an error, it is wrapped as "failed to get compressed stream". This is the custom-compressor branch of the failure.

Source

Thrown at plugins/diff/walking/differ.go:143

						if abortErr := s.store.Abort(ctx, config.Reference); abortErr != nil {
							log.G(ctx).WithError(abortErr).WithField("ref", config.Reference).Warnf("failed to delete diff upload")
						}
					}
				}
			}()
			if !newReference {
				if errOpen = cw.Truncate(0); errOpen != nil {
					return errOpen
				}
			}

			if compressionType != compression.Uncompressed {
				dgstr := digest.SHA256.Digester()
				var compressed io.WriteCloser
				if config.Compressor != nil {
					compressed, errOpen = config.Compressor(cw, config.MediaType)
					if errOpen != nil {
						return fmt.Errorf("failed to get compressed stream: %w", errOpen)
					}
				} else {
					compressed, errOpen = compression.CompressStream(cw, compressionType)
					if errOpen != nil {
						return fmt.Errorf("failed to get compressed stream: %w", errOpen)
					}
				}
				errOpen = archive.WriteDiff(ctx, io.MultiWriter(compressed, dgstr.Hash()), lowerRoot, upperRoot, writeDiffOpts...)
				compressed.Close()
				if errOpen != nil {
					return fmt.Errorf("failed to write compressed diff: %w", errOpen)
				}

				if config.Labels == nil {
					config.Labels = map[string]string{}
				}
				config.Labels[labels.LabelUncompressed] = dgstr.Digest().String()
			} else {

View on GitHub (pinned to 4246446a2b)

Solutions

  1. Inspect the wrapped errOpen from your Compressor function and fix its initialization (unsupported media type, missing dependency).
  2. Ensure the Compressor function matches the configured MediaType (e.g. gzip compressor for gzip media type).
  3. Temporarily nil out config.Compressor to fall back to containerd's built-in compression.CompressStream and confirm the compressor is the culprit.

Example fix

// before
config.Compressor = func(w io.Writer, mediaType string) (io.WriteCloser, error) {
    return zstd.NewWriter(w) // may fail if zstd unsupported
}
// after
config.Compressor = func(w io.Writer, mediaType string) (io.WriteCloser, error) {
    if mediaType != ocispec.MediaTypeImageLayerZstd {
        return nil, fmt.Errorf("unsupported media type %s", mediaType)
    }
    return zstd.NewWriter(w)
}
Defensive patterns

Strategy: validation

Validate before calling

// Exercise the compressor before wiring it in:
wc, err := config.Compressor(io.Discard, config.MediaType)
if err != nil { return fmt.Errorf("compressor unusable: %w", err) }
wc.Close()

Try / catch

if errOpen != nil && strings.Contains(errOpen.Error(), "failed to get compressed stream") {
    // inspect wrapped compressor error; fall back to default CompressStream
}

Prevention

When it happens

Trigger: config.Compressor != nil and the provided compressor function returns an error when invoked with (cw, config.MediaType) during Compare.

Common situations: A custom compressor (e.g. zstd or estargz plugin) that fails to initialize, or a compressor incompatible with the configured media type.

Related errors


AI-assisted analysis of containerd/containerd@4246446a2b (2026-09-02). Data as JSON: /api/errors/70f27f3a7e4fbbda. Report an issue: GitHub.