docker/cli · error

failed to save image

Error message

failed to save image: %w

What it means

Returned by `docker image save` / `docker save` when the atomic output file cannot be created via `atomicwriter.New(opts.output, 0o600)` (save.go:77-79). atomicwriter writes to a temp file then renames, so this fails when the path is invalid, the parent directory is missing, or the process lacks write permission. The %w wraps the underlying OS error (e.g. EACCES, ENOENT, EISDIR).

Solutions

  1. Verify the parent directory of -o exists and is writable: `mkdir -p $(dirname OUT) && touch OUT`.
  2. Ensure -o points to a file path, not an existing directory.
  3. Drop -o and pipe to a writable location: `docker save img > out.tar`.
  4. Check disk space and that the filesystem is mounted read-write.

Example fix

# before
docker save -o /tmp/missing-dir/img.tar myimage
# after
mkdir -p /tmp/missing-dir && docker save -o /tmp/missing-dir/img.tar myimage
Defensive patterns

Strategy: validation

Validate before calling

// validate the -o output path before calling ImageSave
info, err := os.Stat(filepath.Dir(outputPath))
if err != nil || !info.IsDir() {
    return fmt.Errorf("output directory for %q is not accessible: %w", outputPath, err)
}
if f, err := os.Create(outputPath); err != nil {
    return fmt.Errorf("output path %q not writable: %w", outputPath, err)
} else {
    f.Close()
    os.Remove(outputPath)
}

Try / catch

if err := runSave(ctx, cli, opts); err != nil {
    if strings.Contains(err.Error(), "failed to save image") {
        // surface an actionable hint about the -o path
        log.Fatalf("save failed — check that -o path is a writable file in an existing directory: %v", err)
    }
    log.Fatal(err)
}

Prevention

When it happens

Trigger: Running `docker save -o /nonexistent/dir/img.tar img` (parent dir missing), `-o /some/dir` where the path is an existing directory, or `-o /root/file.tar` as a non-root user. Also triggered on read-only filesystems.

Common situations: Typos in the -o path, pointing -o at a directory instead of a file, running in a container/CI where the output mount is read-only, or a path with no permission.

Related errors


AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07). Data as JSON: /api/errors/d50b20b8f7ec7b86. Report an issue: GitHub.

Appendix: source

Thrown at cli/command/image/save.go:79

		if err != nil {
			return fmt.Errorf("invalid platform: %w", err)
		}
		platformList = append(platformList, pp)
	}
	if len(platformList) > 0 {
		options = append(options, client.ImageSaveWithPlatforms(platformList...))
	}

	var output io.Writer
	if opts.output == "" {
		if dockerCLI.Out().IsTerminal() {
			return errors.New("cowardly refusing to save to a terminal. Use the -o flag or redirect")
		}
		output = dockerCLI.Out()
	} else {
		writer, err := atomicwriter.New(opts.output, 0o600)
		if err != nil {
			return fmt.Errorf("failed to save image: %w", err)
		}
		defer writer.Close()
		output = writer
	}

	responseBody, err := dockerCLI.Client().ImageSave(ctx, opts.images, options...)
	if err != nil {
		return err
	}
	defer responseBody.Close()

	_, err = io.Copy(output, responseBody)
	return err
}

View on GitHub (pinned to 4f84911bfe)