nektos/act · error

invalid range format for --expose: %w

Error message

invalid range format for --expose: %w

What it means

Values from --expose may be a single port with optional protocol (<port>/tcp) or a range (<start>-<end>/tcp). Each entry is parsed with network.ParsePortRange; invalid syntax — non-numeric ports, reversed ranges (9000-80), out-of-range port numbers (>65535), or malformed protocol suffixes — fails with this wrapped error.

Source

Thrown at pkg/container/docker_cli.go:481

	}

	// 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 4f41128141)

Solutions

  1. Use plain '<port>' or '<start>-<end>' with optional '/tcp' or '/udp': --expose 8080 or --expose 3000-3005/tcp
  2. Fix reversed ranges to low-high
  3. Keep ports within 1-65535
  4. Remember --expose only opens the port inside the container network; use -p for host publishing

Example fix

# before
options: --expose 8080-80

# after
options: --expose 80-8080/tcp
Defensive patterns

Strategy: validation

Validate before calling

for _, e := range exposeList {
    if _, err := network.ParsePortRange(e); err != nil {
        return fmt.Errorf("bad --expose value %q: %w", e, err)
    }
}

Type guard

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

Prevention

When it happens

Trigger: Passing --expose in container/job options such as '--expose 80-8080-tcp', '--expose tcp://80', '--expose 70000', or '--expose 8080-80'. Parsing happens client-side in act's container option parser before any daemon call.

Common situations: Confusing --expose with -p publish syntax (protocol://host:container forms); ranges written high-low; copy-paste from firewall rules using 65536+ values; YAML coercing '80' to something unexpected.

Related errors


AI-assisted analysis of nektos/act@4f41128141 (2026-08-15). Data as JSON: /api/errors/498f812fbcb4c492. Report an issue: GitHub.