{"record":{"id":"065f52931c69ff16","repo":"siyuan-note/siyuan","slug":"url-has-no-host-065f52","errorCode":null,"errorMessage":"URL has no host","messagePattern":"URL has no host","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"kernel/util/webfetch.go","lineNumber":47,"sourceCode":"\n\t\"github.com/88250/gulu\"\n\t\"github.com/88250/lute\"\n\t\"github.com/siyuan-note/httpclient\"\n)\n\nconst (\n\tmaxWebFetchBytes     = 5 * 1024 * 1024  // text/html, text/plain\n\tmaxWebFetchFileBytes = 10 * 1024 * 1024 // file/image download\n\tmaxWebFetchChars     = 50000\n)\n\nfunc WebFetch(rawURL, format string) (string, error) {\n\tu, err := url.Parse(rawURL)\n\tif err != nil || (u.Scheme != \"http\" && u.Scheme != \"https\") {\n\t\treturn \"\", errors.New(\"URL must start with http:// or https://\")\n\t}\n\tif u.Host == \"\" {\n\t\treturn \"\", errors.New(\"URL has no host\")\n\t}\n\n\tif err := CheckHostSSRF(u.Hostname()); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tresp, err := httpclient.NewBrowserRequest().Get(rawURL)\n\tif err != nil {\n\t\treturn \"\", errors.New(\"fetch failed: \" + err.Error())\n\t}\n\tdefer resp.Body.Close()\n\n\tif resp.StatusCode >= 400 {\n\t\treturn \"\", fmt.Errorf(\"HTTP %d\", resp.StatusCode)\n\t}\n\n\tcontentType := resp.Header.Get(\"Content-Type\")\n\tmaxReadBytes := int64(maxWebFetchBytes)","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/251596fc0de2f9528c00c224252fd073a99973f4/kernel/util/webfetch.go#L29-L65","documentation":"Thrown by WebFetch after the URL parses and has an http(s) scheme, but u.Host is empty. This catches scheme-prefixed but host-less inputs such as 'https:///path' or 'https://:443/path' that pass the scheme check.","triggerScenarios":"Input like 'https:///foo', 'https://:8080/x', or a URL whose authority is empty. The scheme guard passed but the host component is missing.","commonSituations":"Malformed hand-typed URLs; programmatic construction that joined a scheme with a path but no host; a trimming step that removed the host.","solutions":["Validate that the parsed URL has a non-empty Host before calling WebFetch.","Construct URLs with url.URL{Scheme, Host} rather than string concatenation.","Reject empty-host URLs at the input layer with a clear message."],"exampleFix":"// before\nutil.WebFetch(\"https:///some/path\", \"markdown\")\n\n// after\nutil.WebFetch(\"https://example.com/some/path\", \"markdown\")","handlingStrategy":"validation","validationCode":"u, err := url.Parse(rawURL)\nif err != nil || u.Host == \"\" {\n    return errors.New(\"URL must include a host, e.g. https://example.com/path\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build URLs with url.URL{Scheme, Host} fields rather than string concatenation.","Reject inputs like 'https:///path' that have no host before calling WebFetch.","Surface the host requirement in UI validation messages."],"tags":["webfetch","validation","url"],"backgroundTag":null,"analyzedSha":"251596fc0de2f9528c00c224252fd073a99973f4","analyzedAt":"2026-08-12T21:18:37.123Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}