docker/cli · error
invalid bind source, source cannot be empty
Error message
invalid bind source, source cannot be empty
What it means
Returned by handleBindToMount (cli/compose/convert/volume.go:123-124). A bind mount maps an existing host path into the container, so a non-empty source path is mandatory. When type is "bind" but Source is the empty string, the converter cannot construct a valid mount and aborts before reaching the engine.
Solutions
- Add an explicit non-empty `source:` host path to the bind volume entry.
- Make interpolation failures loud: `source: ${HOST_PATH:?HOST_PATH must be set}`.
- If you wanted an anonymous in-container volume, change `type:` to `volume`; if you wanted an in-memory filesystem, change it to `tmpfs`.
Example fix
# before
volumes:
- type: bind
target: /cfg
source: ${HOST_CFG}
# after (require the var)
volumes:
- type: bind
target: /cfg
source: ${HOST_CFG:?HOST_CFG must be set} Defensive patterns
Strategy: validation
Validate before calling
for i, v := range serviceVolumes {
if v.Type == "bind" && strings.TrimSpace(v.Source) == "" {
return fmt.Errorf("service volume[%d] target=%q: bind mount requires a non-empty source", i, v.Target)
}
} Try / catch
mounts, err := convert.Volumes(serviceVolumes, stackVolumes, namespace)
if err != nil {
if strings.Contains(err.Error(), "invalid bind source") {
return fmt.Errorf("compose config error: %w (set source: to a host path; use ${VAR:?msg} for interpolation)", err)
}
return err
} Prevention
- Always set an explicit `source:` for bind mounts.
- Use `${VAR:?error message}` interpolation so unset variables fail loudly instead of producing empty sources.
- Validate the rendered compose file (`docker compose config`) in CI before deploying.
When it happens
Trigger: A ServiceVolumeConfig with Type=="bind" and Source=="" passed to convert.Volumes() / convertVolumeToMount(). Typically a compose entry `type: bind` with no `source:` key, or a `source:` whose interpolation resolved to empty.
Common situations: `source: ${HOST_PATH}` where HOST_PATH is unset/unexported; YAML key typo like `src:` instead of `source:`; stripping the source line during a refactor; inheriting a template that expected a host path variable.
Related errors
- volume options are incompatible with type bind
- image options are incompatible with type bind
- tmpfs options are incompatible with type bind
- cluster options are incompatible with type bind
- bind options are incompatible with type tmpfs
AI-assisted analysis of docker/cli@4f84911bfe (2026-08-07).
Data as JSON: /api/errors/156c6136cbf05081.
Report an issue: GitHub.
Appendix: source
Thrown at cli/compose/convert/volume.go:124
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,
}
}
return result, nil
}
func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
result := createMountFromVolume(volume)
if volume.Source == "" {
return mount.Mount{}, errors.New("invalid bind source, source cannot be empty")
}
if volume.Volume != nil {
return mount.Mount{}, errors.New("volume options are incompatible with type bind")
}
if volume.Image != nil {
return mount.Mount{}, errors.New("image options are incompatible with type bind")
}
if volume.Tmpfs != nil {
return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind")
}
if volume.Cluster != nil {
return mount.Mount{}, errors.New("cluster options are incompatible with type bind")
}
if volume.Bind != nil {
result.BindOptions = &mount.BindOptions{
Propagation: mount.Propagation(volume.Bind.Propagation),
}
}View on GitHub (pinned to 4f84911bfe)