docker/cli · error

invalid range format for --expose

Error message

invalid range format for --expose: %w

What it means

Returned in opts.go:474 when network.ParsePortRange cannot parse a --expose token. --expose accepts either a single port[/proto] or a start-end[/proto] range; anything else fails.

Solutions

  1. Use the form PORT[/PROTO] or STARTPORT-ENDPORT[/PROTO].
  2. Ensure start <= end and ports are within 0-65535.
  3. Check for stray separators (.., --, etc.).

Example fix

# before
docker run --expose 8080..9090 alpine

# after
docker run --expose 8080-9090 alpine
Defensive patterns

Strategy: validation

Validate before calling

import "github.com/docker/docker/api/types/network"
if _, err := network.ParsePortRange(expose); err != nil {
    return fmt.Errorf("bad --expose %q: %w", expose, err)
}

Type guard

func validPortRange(s string) bool {
    _, err := network.ParsePortRange(s)
    return err == nil
}

Try / catch

// Deterministic: fix the token; do not retry.
if !validPortRange(tok) { /* drop or correct the token */ }

Prevention

When it happens

Trigger: `docker run --expose <bad>`: non-numeric port, a range with the start greater than the end, a port above 65535, or malformed separators.

Common situations: Typo like --expose 8080..9090 (double dot), --expose 9000-8000 (reversed range), or --expose abc/tcp.

Related errors


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

Appendix: source

Thrown at cli/command/container/opts.go:474

	}

	// Add published ports as exposed ports.
	exposedPorts := network.PortSet{}
	for port := range ports {
		p, err := network.ParsePort(string(port))
		if err != nil {
			return nil, err
		}
		exposedPorts[p] = struct{}{}
	}

	// Merge in exposed ports to the map of published ports
	for _, e := range copts.expose.GetSlice() {
		// support two formats for expose, original format <portnum>/[<proto>]
		// or <startport-endport>/[<proto>]
		pr, err := network.ParsePortRange(e)
		if err != nil {
			return nil, fmt.Errorf("invalid range format for --expose: %w", err)
		}
		// parse the start and end port and create a sequence of ports to expose
		// if expose a port, the start and end port are the same
		for p := range pr.All() {
			exposedPorts[p] = struct{}{}
		}
	}

	// validate and parse device mappings. Note we do late validation of the
	// device path (as opposed to during flag parsing), as at the time we are
	// parsing flags, we haven't yet sent a _ping to the daemon to determine
	// what operating system it is.
	devices := copts.devices.GetSlice()
	deviceMappings := make([]container.DeviceMapping, 0, len(devices))
	cdiDeviceNames := make([]string, 0, len(devices))
	for _, device := range devices {
		if cdi.IsQualifiedName(device) {
			cdiDeviceNames = append(cdiDeviceNames, device)

View on GitHub (pinned to 4f84911bfe)