{"record":{"id":"aa36b556e874e511","repo":"grpc/grpc-go","slug":"missing-port-after-port-separator-colon","errorCode":null,"errorMessage":"missing port after port-separator colon","messagePattern":"missing port after port-separator colon","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/resolver/delegatingresolver/delegatingresolver.go","lineNumber":245,"sourceCode":"// An error is returned for empty targets or targets with a trailing colon\n// but no port (e.g., \"host:\").\nfunc parseTarget(target string) (string, error) {\n\tif target == \"\" {\n\t\treturn \"\", fmt.Errorf(\"missing address\")\n\t}\n\n\thost, port, err := net.SplitHostPort(target)\n\tif err != nil {\n\t\t// If SplitHostPort fails, it's likely because the port is missing.\n\t\t// We append the default port and return the result.\n\t\treturn net.JoinHostPort(target, defaultPort), nil\n\t}\n\n\t// If SplitHostPort succeeds, we check for edge cases.\n\tif port == \"\" {\n\t\t// A success with an empty port means the target had a trailing colon,\n\t\t// e.g., \"host:\", which is an error.\n\t\treturn \"\", fmt.Errorf(\"missing port after port-separator colon\")\n\t}\n\tif host == \"\" {\n\t\t// A success with an empty host means the target was like \":80\".\n\t\t// We default the host to \"localhost\".\n\t\thost = \"localhost\"\n\t}\n\treturn net.JoinHostPort(host, port), nil\n}\n\nfunc skipProxy(address resolver.Address) bool {\n\t// Avoid proxy when network is not tcp.\n\tnetworkType, ok := networktype.Get(address)\n\tif !ok {\n\t\tnetworkType, _ = transport.ParseDialTarget(address.Addr)\n\t}\n\tif networkType != \"tcp\" {\n\t\treturn true\n\t}","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/internal/resolver/delegatingresolver/delegatingresolver.go#L227-L263","documentation":"Returned by parseTarget when net.SplitHostPort succeeded but the port component is empty, meaning the target ended with a colon and no port (e.g. \"host:\"). This is distinct from a missing port (which gets the default appended): a trailing colon is an explicit, ambiguous, and therefore invalid form.","triggerScenarios":"Target strings like \"host:\", \"[::1]:\", or \"dns:///host:\" where the user typed a colon but no port number after it.","commonSituations":"String formatting bug that appends \":\" unconditionally; template that produces \"host:${PORT}\" with PORT unset; copy-paste from a URL that included a trailing colon.","solutions":["Either remove the trailing colon or supply a real port: \"host:443\".","Fix the code/template that emits the colon so it only does so when a port exists.","Validate targets with a host:port check before dialing."],"exampleFix":"// before\ntarget := host + \":\" + port // port empty -> \"host:\"\n// after\nif port != \"\" { target = host + \":\" + port } else { target = host }","handlingStrategy":"validation","validationCode":"if strings.HasSuffix(target, \":\") {\n    return fmt.Errorf(\"target %q ends with colon but no port\", target)\n}","typeGuard":"func hasValidPortForm(t string) bool {\n    if !strings.Contains(t, \":\") { return true }\n    return !strings.HasSuffix(t, \":\")\n}","tryCatchPattern":"if strings.HasSuffix(target, \":\") {\n    target = strings.TrimSuffix(target, \":\") // recover by dropping stray colon\n}\nconn, err := grpc.Dial(target, ...)","preventionTips":["Format host:port only when a port is actually present.","Use net.JoinHostPort to build targets rather than string concatenation.","Lint target strings in CI to catch trailing colons."],"tags":["grpc","resolver","delegating-resolver","target-uri","validation"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}