{"record":{"id":"948e6d2f7824151b","repo":"usememos/memos","slug":"invalid-url-format","errorCode":null,"errorMessage":"invalid URL format","messagePattern":"invalid URL format","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"warning","filePath":"internal/httpgetter/html_meta.go","lineNumber":111,"sourceCode":"\t\t\treturn nil, errors.Wrapf(ErrInternalIP, \"host=%s, ip=%s\", host, ip.String())\n\t\t}\n\t\tips = append(ips, ip)\n\t}\n\tif len(ips) == 0 {\n\t\treturn nil, errors.New(\"hostname resolved to no addresses\")\n\t}\n\n\treturn ips, nil\n}\n\nfunc isInternalIP(ip net.IP) bool {\n\treturn ip.IsLoopback() || ip.IsPrivate() || ip.IsLinkLocalUnicast() || ip.IsUnspecified()\n}\n\nfunc validateURL(urlStr string) error {\n\tu, err := url.Parse(urlStr)\n\tif err != nil {\n\t\treturn errors.New(\"invalid URL format\")\n\t}\n\n\tif u.Scheme != \"http\" && u.Scheme != \"https\" {\n\t\treturn errors.New(\"only http/https protocols are allowed\")\n\t}\n\n\thost := u.Hostname()\n\tif host == \"\" {\n\t\treturn errors.New(\"empty hostname\")\n\t}\n\n\tif ip := net.ParseIP(host); ip != nil && isInternalIP(ip) {\n\t\treturn errors.Wrap(ErrInternalIP, ip.String())\n\t}\n\n\treturn nil\n}\n","sourceCodeStart":93,"sourceCodeEnd":129,"githubUrl":"https://github.com/usememos/memos/blob/14d757ce1fb31c78590f374bc042f8dbedbc20d7/internal/httpgetter/html_meta.go#L93-L129","documentation":"validateURL in internal/httpgetter fails at url.Parse: the string cannot be parsed as a URL at all (Go's parser is lenient, so this usually means control characters, spaces in the scheme, or a completely malformed input). This runs before scheme, host, and IP checks.","triggerScenarios":"Passing a bare string like `example.com/page` (no scheme can still parse, but inputs with control chars/invalid escapes fail), URLs with embedded control characters or malformed percent-encoding, or non-URL text accidentally submitted as a link.","commonSituations":"Users pasting text instead of a URL into a memo that gets auto-detected as a link; frontends not validating input before calling the preview API; copy-paste introducing invisible control characters (e.g. from PDFs or chat apps).","solutions":["Sanitize/trim the URL string, remove control characters, ensure valid percent-encoding","Always include scheme and host: `https://example.com/path`","Validate with url.Parse on the client side before submitting"],"exampleFix":"// before\nGetHTMLMeta(\"example.com/page\\x00\")\n// after\nGetHTMLMeta(\"https://example.com/page\")","handlingStrategy":"validation","validationCode":"// Normalize + parse before fetching\nfunc normalizeURL(raw string) (string, error) {\n  raw = strings.TrimSpace(raw)\n  raw = strings.Map(func(r rune) rune { if r < 0x20 { return -1 }; return r }, raw) // strip control chars\n  u, err := url.Parse(raw)\n  if err != nil { return \"\", err }\n  if u.Host == \"\" { return \"\", errors.New(\"missing host\") }\n  return u.String(), nil\n}","typeGuard":null,"tryCatchPattern":"// Wrap fetch and treat URL validation failures as user-input errors\nif _, err := getter.GetHTMLMeta(raw); err != nil {\n  if strings.Contains(err.Error(), \"invalid URL format\") {\n    return status.Errorf(codes.InvalidArgument, \"not a valid URL: %s\", raw)\n  }\n  return err\n}","preventionTips":["Trim and strip control characters from pasted URLs client-side","Always include scheme and host","Run url.Parse client-side before calling preview APIs"],"tags":["network","url","validation"],"backgroundTag":null,"analyzedSha":"14d757ce1fb31c78590f374bc042f8dbedbc20d7","analyzedAt":"2026-08-15T09:27:36.538Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}