docker/cli · error

invalid image source, source cannot be empty

Error message

invalid image source, source cannot be empty

What it means

Returned by handleImageToMount (volume.go:97-98) when a Compose service volume is type: image but has an empty 'source'. An image mount must name the image to mount from (the source field), so an empty source is rejected before the engine call. This mirrors the same pattern used for bind/npipe/cluster mounts requiring a non-empty source.

Solutions

  1. Add a 'source' field naming the image, e.g. source: myimg:latest.
  2. Switch to long-form and set type: image plus source explicitly.
  3. Validate with 'docker compose config'.

Example fix

# before
volumes:
  - type: image
    target: /app

# after
volumes:
  - type: image
    source: myrepo/myimg:latest
    target: /app
Defensive patterns

Strategy: validation

Validate before calling

// Ensure an image-type mount has a non-empty source.
func validateImageMount(v composetypes.ServiceVolumeConfig) error {
    if v.Type == "image" && v.Source == "" {
        return errors.New("image mount requires a non-empty source (the image reference)")
    }
    return nil
}

Type guard

// imageMountHasSource reports whether an image-type mount names its source image.
func imageMountHasSource(v composetypes.ServiceVolumeConfig) bool {
    if v.Type != "image" {
        return true
    }
    return strings.TrimSpace(v.Source) != ""
}

Prevention

When it happens

Trigger: In docker-compose.yml: { type: image, target: /app } with no source, or { type: image, source: "", target: /app }. The check at line 97 sees volume.Source == "" and returns the error.

Common situations: Using the short syntax '- /app' (which defaults to volume type) but intending an image mount. Forgetting the image reference when converting from a Dockerfile ref. Templating that emits an empty source.

Related errors


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

Appendix: source

Thrown at cli/compose/convert/volume.go:98

		return result, nil
	}

	result.VolumeOptions.Labels = addStackLabel(namespace, stackVolume.Labels)
	if stackVolume.Driver != "" || stackVolume.DriverOpts != nil {
		result.VolumeOptions.DriverConfig = &mount.Driver{
			Name:    stackVolume.Driver,
			Options: stackVolume.DriverOpts,
		}
	}

	return result, nil
}

func handleImageToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
	result := createMountFromVolume(volume)

	if volume.Source == "" {
		return mount.Mount{}, errors.New("invalid image source, source cannot be empty")
	}
	if volume.Volume != nil {
		return mount.Mount{}, errors.New("volume options are incompatible with type image")
	}
	if volume.Bind != nil {
		return mount.Mount{}, errors.New("bind options are incompatible with type image")
	}
	if volume.Tmpfs != nil {
		return mount.Mount{}, errors.New("tmpfs options are incompatible with type image")
	}
	if volume.Cluster != nil {
		return mount.Mount{}, errors.New("cluster options are incompatible with type image")
	}
	if volume.Image != nil {
		result.ImageOptions = &mount.ImageOptions{
			Subpath: volume.Image.Subpath,
		}
	}

View on GitHub (pinned to 4f84911bfe)