sipeed/picoclaw · error

port cannot be empty

Error message

port cannot be empty

What it means

netbind.OpenPlan rejects an empty port string before opening any listeners. Port must be a non-empty string; "0" is the explicit way to request an ephemeral port (OpenPlan latches the OS-assigned port from the first group when port=="0").

Source

Thrown at pkg/netbind/netbind.go:317

			seenExact[key] = struct{}{}
			groups = append(groups, bindGroup{
				kind: groupExact,
				exact: exactBinding{
					host:    token.canonical,
					network: "tcp",
				},
			})
		}
	}

	plan := Plan{groups: groups}
	plan.ProbeHost = probeHostForGroups(groups)
	return plan, nil
}

func OpenPlan(plan Plan, port string) (OpenResult, error) {
	if port == "" {
		return OpenResult{}, errors.New("port cannot be empty")
	}

	selectedPort := port
	listeners := make([]net.Listener, 0, len(plan.groups))
	bindHosts := make([]string, 0, len(plan.groups))
	bindSeen := make(map[string]struct{}, len(plan.groups))

	closeAll := func() {
		for _, ln := range listeners {
			_ = ln.Close()
		}
	}

	for _, group := range plan.groups {
		groupListeners, groupHosts, actualPort, err := openGroup(group, selectedPort)
		if err != nil {
			closeAll()
			return OpenResult{}, err

View on GitHub (pinned to 49183d7e8d)

Solutions

  1. Pass "0" when you want the OS to pick a free port
  2. Pass the concrete port string from config (e.g. "8000")
  3. If port comes from an int, map 0 to "0": port := strconv.Itoa(cfg.Port)

Example fix

// before
res, err := netbind.OpenPlan(plan, cfg.BindPort) // cfg.BindPort == ""

// after
port := cfg.BindPort
if port == "" { port = "0" } // ephemeral
res, err := netbind.OpenPlan(plan, port)
Defensive patterns

Strategy: validation

Validate before calling

port := strings.TrimSpace(cfg.BindPort)
if port == "" {
    port = "0" // ephemeral
}
res, err := netbind.OpenPlan(plan, port)

Try / catch

res, err := netbind.OpenPlan(plan, port)
if err != nil {
    if err.Error() == "port cannot be empty" { /* fix port source, not a runtime condition */ }
    return err
}

Prevention

When it happens

Trigger: OpenPlan(plan, "") — typically a config field (port unset/empty) or a zero-valued variable formatted to "" instead of "0" being passed straight through.

Common situations: Config schema allows an omitted port and the empty string reaches the binder; code converting an int port to string skips the 0 case; tests constructing a Plan manually and forgetting the port.

Related errors


AI-assisted analysis of sipeed/picoclaw@49183d7e8d (2026-08-15). Data as JSON: /api/errors/6e408039de431a8d. Report an issue: GitHub.