{"record":{"id":"6e408039de431a8d","repo":"sipeed/picoclaw","slug":"port-cannot-be-empty","errorCode":null,"errorMessage":"port cannot be empty","messagePattern":"port cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/netbind/netbind.go","lineNumber":317,"sourceCode":"\t\t\tseenExact[key] = struct{}{}\n\t\t\tgroups = append(groups, bindGroup{\n\t\t\t\tkind: groupExact,\n\t\t\t\texact: exactBinding{\n\t\t\t\t\thost:    token.canonical,\n\t\t\t\t\tnetwork: \"tcp\",\n\t\t\t\t},\n\t\t\t})\n\t\t}\n\t}\n\n\tplan := Plan{groups: groups}\n\tplan.ProbeHost = probeHostForGroups(groups)\n\treturn plan, nil\n}\n\nfunc OpenPlan(plan Plan, port string) (OpenResult, error) {\n\tif port == \"\" {\n\t\treturn OpenResult{}, errors.New(\"port cannot be empty\")\n\t}\n\n\tselectedPort := port\n\tlisteners := make([]net.Listener, 0, len(plan.groups))\n\tbindHosts := make([]string, 0, len(plan.groups))\n\tbindSeen := make(map[string]struct{}, len(plan.groups))\n\n\tcloseAll := func() {\n\t\tfor _, ln := range listeners {\n\t\t\t_ = ln.Close()\n\t\t}\n\t}\n\n\tfor _, group := range plan.groups {\n\t\tgroupListeners, groupHosts, actualPort, err := openGroup(group, selectedPort)\n\t\tif err != nil {\n\t\t\tcloseAll()\n\t\t\treturn OpenResult{}, err","sourceCodeStart":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/netbind/netbind.go#L299-L335","documentation":"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\").","triggerScenarios":"OpenPlan(plan, \"\") — typically a config field (port unset/empty) or a zero-valued variable formatted to \"\" instead of \"0\" being passed straight through.","commonSituations":"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.","solutions":["Pass \"0\" when you want the OS to pick a free port","Pass the concrete port string from config (e.g. \"8000\")","If port comes from an int, map 0 to \"0\": port := strconv.Itoa(cfg.Port)"],"exampleFix":"// before\nres, err := netbind.OpenPlan(plan, cfg.BindPort) // cfg.BindPort == \"\"\n\n// after\nport := cfg.BindPort\nif port == \"\" { port = \"0\" } // ephemeral\nres, err := netbind.OpenPlan(plan, port)","handlingStrategy":"validation","validationCode":"port := strings.TrimSpace(cfg.BindPort)\nif port == \"\" {\n    port = \"0\" // ephemeral\n}\nres, err := netbind.OpenPlan(plan, port)","typeGuard":null,"tryCatchPattern":"res, err := netbind.OpenPlan(plan, port)\nif err != nil {\n    if err.Error() == \"port cannot be empty\" { /* fix port source, not a runtime condition */ }\n    return err\n}","preventionTips":["Normalize empty port to \"0\" at the config layer, not at the bind layer","When converting int ports, handle 0 explicitly with strconv.Itoa","Unit-test the binder with the exact config values production sends"],"tags":["netbind","networking","validation"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}