{"record":{"id":"88b3293d184ceaa0","repo":"charmbracelet/glow","slug":"unable-to-parse-json-w","errorCode":null,"errorMessage":"unable to parse json: %w","messagePattern":"unable to parse json: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"github.go","lineNumber":40,"sourceCode":"\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)\n\t}\n\n\tvar result readme\n\tif err := json.Unmarshal(body, &result); err != nil {\n\t\treturn nil, fmt.Errorf(\"unable to parse json: %w\", err)\n\t}\n\n\tif res.StatusCode == http.StatusOK {\n\t\t//nolint:bodyclose\n\t\t// it is closed on the caller\n\t\tresp, err := http.Get(result.DownloadURL) //nolint: noctx\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to get url: %w\", err)\n\t\t}\n\n\t\tif resp.StatusCode == http.StatusOK {\n\t\t\treturn &source{resp.Body, result.DownloadURL}, nil\n\t\t}\n\t}\n\n\treturn nil, errors.New(\"can't find README in GitHub repository\")\n}\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/charmbracelet/glow/blob/e3970c813d93da1ea84edfca90c289d9afb82242/github.go#L22-L58","documentation":"The API body is unmarshalled with json.Unmarshal into a small struct with only download_url. This error means the bytes received were not valid JSON — glow does not check Content-Type or status before parsing, so any non-JSON 200-class or error page that reaches this point fails here. Note the status check happens after parsing, so even a 404 JSON body would parse fine and only fail later.","triggerScenarios":"A captive portal or transparent proxy returning an HTML login/interception page; DNS or hosts-file entries sending api.{hostname} to a server that answers 200 with HTML; GitLab/GitHub mirrors or gateways that return plain-text errors; truncated bodies from intermediary truncation that still exit ReadAll cleanly.","commonSituations":"Hotel/airport Wi-Fi captive portals; corporate SSL-inspection appliances rewriting responses; custom hosts entries like 127.0.0.1 api.github.com during testing; GitHub Enterprise hosts routed into this function whose API prefix differs.","solutions":["Inspect what is actually returned: curl -s https://api.github.com/repos/OWNER/REPO/readme | head -c 200","Remove/fix hosts-file or DNS overrides for api.github.com","Accept the network's portal/proxy terms or route around the interception","Use the raw.githubusercontent.com URL directly to sidestep the API response"],"exampleFix":"# before (hosts file maps api.github.com to a local stub serving HTML)\nglow https://github.com/owner/repo\n# Error: unable to parse json: invalid character '<' looking for beginning of value\n\n# after\n$ sudo sed -i '/api\\.github\\.com/d' /etc/hosts && glow https://github.com/owner/repo","handlingStrategy":"try-catch","validationCode":"// sniff the body before JSON parsing: only parse responses that look like JSON\nif len(body) == 0 || (body[0] != '{' && body[0] != '[') {\n\treturn nil, fmt.Errorf(\"non-JSON response (first bytes: %.40q) — captive portal or proxy interception?\", string(body))\n}","typeGuard":null,"tryCatchPattern":"var result readme\nif err := json.Unmarshal(body, &result); err != nil {\n\tvar synErr *json.SyntaxError\n\tif errors.As(err, &synErr) {\n\t\t// capture evidence for diagnosis instead of a bare 'parse' failure\n\t\tsnippet := string(body)\n\t\tif len(snippet) > 120 {\n\t\t\tsnippet = snippet[:120]\n\t\t}\n\t\treturn nil, fmt.Errorf(\"unable to parse json at offset %d (body starts with: %s): %w\", synErr.Offset, snippet, err)\n\t}\n\tvar typeErr *json.UnmarshalTypeError\n\tif errors.As(err, &typeErr) {\n\t\treturn nil, fmt.Errorf(\"API schema changed: field %s: %w\", typeErr.Field, err)\n\t}\n\treturn nil, fmt.Errorf(\"unable to parse json: %w\", err)\n}","preventionTips":["Check Content-Type and the first byte of the body before unmarshalling — '<' almost always means an HTML interception page","Keep hosts-file/DNS overrides for api.github.com out of machines that run glow","Complete captive-portal logins or exempt api.github.com from SSL inspection before automating fetches"],"tags":["json","parsing","proxy","captive-portal","github","api"],"backgroundTag":null,"analyzedSha":"e3970c813d93da1ea84edfca90c289d9afb82242","analyzedAt":"2026-08-15T22:50:54.304Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}