{"record":{"id":"eb5e16d624734b39","repo":"sipeed/picoclaw","slug":"host-cannot-be-empty","errorCode":null,"errorMessage":"host cannot be empty","messagePattern":"host cannot be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/netbind/netbind.go","lineNumber":426,"sourceCode":"\t\treturn ResolveAdaptiveLoopbackHost()\n\tcase hasIPv6Any:\n\t\treturn \"::1\"\n\tcase hasIPv4Any:\n\t\treturn \"127.0.0.1\"\n\t}\n\n\tfor _, group := range groups {\n\t\tif group.kind == groupExact {\n\t\t\treturn group.exact.host\n\t\t}\n\t}\n\treturn ResolveAdaptiveLoopbackHost()\n}\n\nfunc parseHostTokens(raw string) ([]hostToken, error) {\n\traw = strings.TrimSpace(raw)\n\tif raw == \"\" {\n\t\treturn nil, errors.New(\"host cannot be empty\")\n\t}\n\n\tparts := strings.Split(raw, \",\")\n\ttokens := make([]hostToken, 0, len(parts))\n\tseen := make(map[string]struct{}, len(parts))\n\tfor _, part := range parts {\n\t\ttoken, err := parseHostToken(part)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif _, ok := seen[token.key]; ok {\n\t\t\tcontinue\n\t\t}\n\t\tseen[token.key] = struct{}{}\n\t\ttokens = append(tokens, token)\n\t}\n\n\tif len(tokens) == 0 {","sourceCodeStart":408,"sourceCodeEnd":444,"githubUrl":"https://github.com/sipeed/picoclaw/blob/49183d7e8daed0dba89ddbb6fcb60089401d9680/pkg/netbind/netbind.go#L408-L444","documentation":"parseHostTokens rejects a host specification that is empty after strings.TrimSpace. This parser turns a comma-separated host list (\"localhost\", \"*\", IPs, names) into bind tokens for building a netbind Plan, so an entirely blank spec cannot produce a bind group.","triggerScenarios":"Building a bind Plan from a host spec string that is \"\" or whitespace-only — e.g. a config hosts field left unset and defaulted to \"\" instead of a real value like \"localhost\".","commonSituations":"Optional bind-host config mapped verbatim to the parser; YAML null/empty scalar for the hosts key; tests passing an empty string for 'any host' when they meant \"*\" or \"0.0.0.0\".","solutions":["Provide at least one host token: \"localhost\", \"*\", an IP, or a resolvable name","If the intent was 'all interfaces', pass \"*\" (or \"0.0.0.0,::\") rather than an empty string","Default the config value before parsing: if strings.TrimSpace(hosts) == \"\" { hosts = \"localhost\" }"],"exampleFix":"// before\nplan, err := netbind.NewPlan(\"\") // or whatever wraps parseHostTokens\n\n// after\nplan, err := netbind.NewPlan(\"localhost\")\n// or \"*\" for all interfaces","handlingStrategy":"validation","validationCode":"hosts := strings.TrimSpace(cfg.BindHosts)\nif hosts == \"\" {\n    hosts = \"localhost\" // or \"*\" for all interfaces\n}\nplan, err := netbind.NewPlan(hosts)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Give the hosts config field a non-empty default","Distinguish 'unspecified' (use a default) from 'all interfaces' (use \"*\") in your config schema","Validate config strings with TrimSpace before passing to netbind"],"tags":["netbind","networking","validation","configuration"],"backgroundTag":null,"analyzedSha":"49183d7e8daed0dba89ddbb6fcb60089401d9680","analyzedAt":"2026-08-15T21:55:41.315Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}