dagger/dagger · error

llbtodagger: invalid exposed port %q: %w

Error message

llbtodagger: invalid exposed port %q: %w

What it means

Each key of the config's ExposedPorts map must parse as <port> or <port>/<tcp|udp>. parseExposedPort validates the numeric port and protocol; any failure (non-numeric port, out-of-range port, unknown protocol) is wrapped with this message identifying the raw key.

Source

Thrown at util/llbtodagger/metadata.go:65

		)
	}

	labelKeys := sortedMapKeys(cfg.Labels)
	for _, key := range labelKeys {
		ctrID = appendCall(
			ctrID,
			containerType(),
			"withLabel",
			argString("name", key),
			argString("value", cfg.Labels[key]),
		)
	}

	exposedPorts := sortedMapKeys(cfg.ExposedPorts)
	for _, raw := range exposedPorts {
		port, proto, err := parseExposedPort(raw)
		if err != nil {
			return nil, fmt.Errorf("llbtodagger: invalid exposed port %q: %w", raw, err)
		}
		ctrID = appendCall(
			ctrID,
			containerType(),
			"withExposedPort",
			argInt("port", int64(port)),
			argEnum("protocol", proto),
		)
	}

	if cfg.Entrypoint == nil {
		ctrID = appendCall(
			ctrID,
			containerType(),
			"withoutEntrypoint",
			argBool("keepDefaultArgs", true),
		)
	} else {

View on GitHub (pinned to 82ba2681db)

Solutions

  1. Correct the ExposedPorts key in the image config to "<1-65535>" optionally followed by "/tcp" or "/udp" and re-push
  2. Inspect the image with crane config or docker inspect to find the offending port key
  3. Rebuild the image from source with valid EXPOSE instructions

Example fix

// before (image config)
"ExposedPorts": {"8080/http": {}}
// after
"ExposedPorts": {"8080/tcp": {}}
Defensive patterns

Strategy: validation

Validate before calling

cfg, _ := crane.Config(ctx, ref)
re := regexp.MustCompile(`^(\d+)(/(tcp|udp))?$`)
for p := range cfg.ExposedPorts {
    m := re.FindStringSubmatch(p)
    if m == nil || n, _ := strconv.Atoi(m[1]); n < 1 || n > 65535 {
        return fmt.Errorf("image %s has invalid exposed port %q", ref, p)
    }
}

Type guard

var exposedPortRe = regexp.MustCompile(`^([1-9]\d*/?(tcp|udp)?|0*)`)
func validExposedPortKey(raw string) bool {
    port, proto, ok := strings.Cut(raw, "/")
    if ok && !strings.EqualFold(proto, "tcp") && !strings.EqualFold(proto, "udp") {
        return false
    }
    n, err := strconv.Atoi(port)
    return err == nil && n >= 1 && n <= 65535
}

Try / catch

ctr, err := client.Container().From(ctx, ref)
if err != nil && strings.Contains(err.Error(), "invalid exposed port") {
    return fmt.Errorf("image %s declares an unusable exposed port: %w", ref, err)
}

Prevention

When it happens

Trigger: An ExposedPorts map key like "8080/http", "foo/tcp", "99999", or "0" present in the image config while applyDockerImageConfig converts an image source.

Common situations: Hand-crafted or corrupted OCI manifests with unusual EXPOSE syntax; images imported from foreign registries with nonstandard port notation; typos in generated configs (e.g. "8.0.0.0/tcp").

Related errors


AI-assisted analysis of dagger/dagger@82ba2681db (2026-09-05). Data as JSON: /api/errors/a6b731cfa88066d0. Report an issue: GitHub.