docker/cli · error

invalid spec

Error message

invalid spec: %s: %w

What it means

Parse fails when populateFieldFromBuffer returns an error while splitting the spec on ':' — specifically "empty section between colons" (e.g. "a::b" or a leading/trailing colon) or "too many colons" (more than three colon-separated sections). The wrapped error names which rule was broken.

Solutions

  1. Count the colons: at most src:dst:options (3 sections).
  2. Ensure no section is empty (no leading/trailing/double colons).
  3. For complex mounts, switch to the --mount type=... syntax which is unambiguous.
  4. On Windows, use the proper drive-letter form or --mount to avoid path/colon ambiguity.

Example fix

# before
docker run -v /data::/data myimg        # empty section

# after
docker run -v /data:/data:ro myimg
# or, unambiguous:
docker run --mount type=bind,source=/data,destination=/data,readonly myimg
Defensive patterns

Strategy: validation

Validate before calling

// Lightweight pre-check for the common colon mistakes.
func looksLikeValidSpec(s string) error {
    parts := strings.Split(s, ":")
    if len(parts) > 3 {
        return fmt.Errorf("too many colons in %q", s)
    }
    for _, p := range parts {
        if p == "" {
            return fmt.Errorf("empty section between colons in %q", s)
        }
    }
    return nil
}

Prevention

When it happens

Trigger: A volume mount spec with an empty field between colons or more than three colon-separated parts, e.g. -v a::b, -v /s:/d:ro:extra, or a stray leading colon.

Common situations: Typing -v with extra colons; Windows absolute paths confusing the splitter; forgetting source or target; mixing option syntax.

Related errors


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

Appendix: source

Thrown at internal/volumespec/volumespec.go:40

	switch len(spec) {
	case 0:
		return volume, errors.New("invalid empty volume spec")
	case 1, 2:
		volume.Target = spec
		volume.Type = string(mount.TypeVolume)
		return volume, nil
	}

	buffer := make([]rune, 0, len(spec))
	for _, char := range spec + string(endOfSpec) {
		switch {
		case isWindowsDrive(buffer, char):
			buffer = append(buffer, char)
		case char == ':' || char == endOfSpec:
			if err := populateFieldFromBuffer(char, buffer, &volume); err != nil {
				populateType(&volume)
				return volume, fmt.Errorf("invalid spec: %s: %w", spec, err)
			}
			buffer = buffer[:0] // reset, but reuse capacity
		default:
			buffer = append(buffer, char)
		}
	}

	populateType(&volume)
	return volume, nil
}

func isWindowsDrive(buffer []rune, char rune) bool {
	return char == ':' && len(buffer) == 1 && unicode.IsLetter(buffer[0])
}

func populateFieldFromBuffer(char rune, buffer []rune, volume *VolumeConfig) error {
	strBuffer := string(buffer)
	switch {

View on GitHub (pinned to 4f84911bfe)