{"record":{"id":"aac5fb5bd7f9db6c","repo":"charmbracelet/glow","slug":"invalid-url-s","errorCode":null,"errorMessage":"invalid url: %s","messagePattern":"invalid url: (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"github.go","lineNumber":17,"sourceCode":"package main\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n)\n\n// findGitHubREADME tries to find the correct README filename in a repository using GitHub API.\nfunc findGitHubREADME(u *url.URL) (*source, error) {\n\towner, repo, ok := strings.Cut(strings.TrimPrefix(u.Path, \"/\"), \"/\")\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"invalid url: %s\", u.String())\n\t}\n\n\ttype readme struct {\n\t\tDownloadURL string `json:\"download_url\"`\n\t}\n\n\tapiURL := fmt.Sprintf(\"https://api.%s/repos/%s/%s/readme\", u.Hostname(), owner, repo)\n\n\t//nolint:bodyclose\n\t// it is closed on the caller\n\tres, err := http.Get(apiURL) //nolint: gosec,noctx\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to get url: %w\", err)\n\t}\n\n\tbody, err := io.ReadAll(res.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to read http response body: %w\", err)","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/charmbracelet/glow/blob/e3970c813d93da1ea84edfca90c289d9afb82242/github.go#L1-L35","documentation":"findGitHubREADME extracts the owner and repo by cutting the URL path on the first '/' after trimming the leading slash: strings.Cut(strings.TrimPrefix(u.Path, \"/\"), \"/\"). If there is no separator — i.e. fewer than two path segments — ok is false and this error reports the whole URL. It is a pure URL-shape failure that happens before any network request.","triggerScenarios":"Passing a bare host like https://github.com; a profile URL with a single segment like https://github.com/charmbracelet; trailing content that still leaves only one segment; URLs where the repo part is empty (https://github.com/owner/).","commonSituations":"Copy-pasting an incomplete URL from a trimmed tooltip; passing a user/org page instead of a repository; constructing URLs programmatically with an empty repo variable.","solutions":["Use the full repository URL: https://github.com/OWNER/REPO","If building URLs in code, validate both segments are non-empty before invoking glow","For a user/org page, navigate to an actual repo first"],"exampleFix":"# before\nglow https://github.com/charmbracelet   # org page, no repo\n\n# after\nglow https://github.com/charmbracelet/glow","handlingStrategy":"type-guard","validationCode":null,"typeGuard":"func isGitHubRepoURL(u *url.URL) bool {\n\tif u == nil || (u.Host != \"github.com\" && !strings.HasSuffix(u.Host, \".github.com\")) {\n\t\treturn false\n\t}\n\t_, _, ok := strings.Cut(strings.TrimPrefix(u.Path, \"/\"), \"/\")\n\treturn ok\n}\n\nif !isGitHubRepoURL(u) {\n\treturn fmt.Errorf(\"expected https://github.com/OWNER/REPO, got %s\", u)\n}","tryCatchPattern":"src, err := findGitHubREADME(u)\nif err != nil {\n\tvar invErr *url.Error\n\tif strings.Contains(err.Error(), \"invalid url\") {\n\t\t// shape failure — retrying is pointless; re-prompt for a full repo URL\n\t\treturn nil, fmt.Errorf(\"need owner/repo URL like https://github.com/OWNER/REPO: %w\", err)\n\t}\n\t_ = invErr\n\treturn nil, err\n}","preventionTips":["Validate URL shape before rendering: require at least owner and repo path segments","When templating URLs in scripts, assert the repo variable is non-empty","Prefer copy-paste from the browser address bar on the repo root page"],"tags":["url","validation","github","parsing"],"backgroundTag":null,"analyzedSha":"e3970c813d93da1ea84edfca90c289d9afb82242","analyzedAt":"2026-08-15T22:50:54.304Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}