{"record":{"id":"16948329f1b36280","repo":"gastownhall/beads","slug":"s-url-must-include-a-hostname","errorCode":null,"errorMessage":"%s:// URL must include a hostname","messagePattern":"(.+?):// URL must include a hostname","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/remotecache/url.go","lineNumber":148,"sourceCode":"\t}\n\n\t// Scheme-specific structural validation\n\tswitch scheme {\n\tcase \"dolthub\":\n\t\t// dolthub://org/repo — requires org and repo\n\t\tp := strings.TrimPrefix(parsed.Path, \"/\")\n\t\thost := parsed.Host\n\t\tcombined := host\n\t\tif p != \"\" {\n\t\t\tcombined = host + \"/\" + p\n\t\t}\n\t\tparts := strings.Split(combined, \"/\")\n\t\tif len(parts) < 2 || parts[0] == \"\" || parts[1] == \"\" {\n\t\t\treturn fmt.Errorf(\"dolthub:// URL must have org/repo format (e.g., dolthub://myorg/myrepo)\")\n\t\t}\n\tcase \"https\", \"http\", \"git+https\", \"git+http\":\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"%s:// URL must include a hostname\", scheme)\n\t\t}\n\tcase \"ssh\", \"git\", \"git+ssh\":\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"%s:// URL must include a hostname\", scheme)\n\t\t}\n\tcase \"s3\", \"aws\", \"gs\":\n\t\t// s3://bucket/path, aws://bucket/path, gs://bucket/path — host is the bucket\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"%s:// URL must include a bucket name\", scheme)\n\t\t}\n\tcase \"az\":\n\t\t// az://account.blob.core.windows.net/container/path\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"az:// URL must include a storage account hostname\")\n\t\t}\n\tcase \"oci\":\n\t\tif parsed.Host == \"\" {\n\t\t\treturn fmt.Errorf(\"oci:// URL must include a namespace or bucket host\")","sourceCodeStart":130,"sourceCodeEnd":166,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/remotecache/url.go#L130-L166","documentation":"http(s) and git+http(s) remote URLs must include a hostname. After url.Parse, parsed.Host is empty for forms like \"https:///path\" or \"https://\", so the URL cannot address a server.","triggerScenarios":"ValidateRemoteURL with \"https:///org/repo\", \"http://\", \"git+https:///repo\", or a URL where the host was accidentally dropped during substitution.","commonSituations":"Config templates with an unfilled host variable, stripping the host when normalizing URLs, or writing relative URLs where absolute ones are required.","solutions":["Add the hostname: \"https://example.com/org/repo\"","Check env/config interpolation for an empty HOST/ENDPOINT variable","Use the fully qualified domain (or localhost with port) instead of a scheme-only URL"],"exampleFix":"// before\nremote := \"https:///org/repo\"\n// after\nremote := \"https://dolthub.com/org/repo\"","handlingStrategy":"validation","validationCode":"func hasHTTPHost(u string) bool {\n\tscheme, _, ok := strings.Cut(u, \"://\")\n\tif !ok || !strings.HasPrefix(scheme, \"http\") { return false }\n\tparsed, err := url.Parse(u)\n\treturn err == nil && parsed.Host != \"\"\n}","typeGuard":"func httpURLWithHost(s string) (*url.URL, bool) {\n\tu, err := url.Parse(s)\n\tif err != nil || u.Host == \"\" || (u.Scheme != \"https\" && u.Scheme != \"http\") {\n\t\treturn nil, false\n\t}\n\treturn u, true\n}","tryCatchPattern":"if err := remotecache.ValidateRemoteURL(u); err != nil {\n\tif strings.Contains(err.Error(), \"must include a hostname\") {\n\t\treturn fmt.Errorf(\"remote %q is missing a host; expected https://host/path\", u)\n\t}\n}","preventionTips":["Assemble HTTP remotes from explicit host+path variables and check the host is set","Fail config parsing when an endpoint variable is empty","Prefer url.JoinHostPort or url.URL struct construction over string concatenation"],"tags":["url-validation","hostname","http"],"backgroundTag":"invalid-remote-url-format","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}