charmbracelet/glow · error
unable to parse json: %w
Error message
unable to parse json: %w
What it means
After reading the GitLab API response, glow unmarshals it into a small readme struct (expecting a readme_url field) with json.Unmarshal. This error fires when the body is not valid JSON at all - typically an HTML login page, a rate-limit interstitial, or an empty body. GitLab's own JSON error payloads like {"message":"404 Project Not Found"} parse without error (into zero-value fields), so the failure specifically means non-JSON content arrived.
Source
Thrown at gitlab.go:42
}
apiURL := fmt.Sprintf("https://%s/api/v4/projects/%s", u.Hostname(), projectPath)
//nolint:bodyclose
// it is closed on the caller
res, err := http.Get(apiURL) //nolint: gosec,noctx
if err != nil {
return nil, fmt.Errorf("unable to get url: %w", err)
}
body, err := io.ReadAll(res.Body)
if err != nil {
return nil, fmt.Errorf("unable to read http response body: %w", err)
}
var result readme
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("unable to parse json: %w", err)
}
readmeRawURL := strings.ReplaceAll(result.ReadmeURL, "blob", "raw")
if res.StatusCode == http.StatusOK {
//nolint:bodyclose
// it is closed on the caller
resp, err := http.Get(readmeRawURL) //nolint: gosec,noctx
if err != nil {
return nil, fmt.Errorf("unable to get url: %w", err)
}
if resp.StatusCode == http.StatusOK {
return &source{resp.Body, readmeRawURL}, nil
}
}
return nil, errors.New("can't find README in GitLab repository")View on GitHub (pinned to e3970c813d)
Solutions
- curl -i the exact API URL to see the real status code and body
- If the instance requires authentication, use a raw file URL, clone the repo, or publish the project - this code path has no token support
- Wait and retry if the body shows a rate-limit or challenge page
- Confirm the host actually runs GitLab
Example fix
// before
body, err := io.ReadAll(res.Body)
if err != nil { return nil, fmt.Errorf("unable to read http response body: %w", err) }
var result readme
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("unable to parse json: %w", err)
}
// after
if res.StatusCode != http.StatusOK {
return nil, fmt.Errorf("gitlab api status %d", res.StatusCode)
}
if ct := res.Header.Get("Content-Type"); !strings.Contains(ct, "application/json") {
return nil, fmt.Errorf("unexpected content-type %q", ct)
}
var result readme
if err := json.Unmarshal(body, &result); err != nil {
return nil, fmt.Errorf("unable to parse json: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
func isJSONResponse(res *http.Response, body []byte) bool {
if res.StatusCode != http.StatusOK { return false }
if ct := res.Header.Get("Content-Type"); !strings.Contains(ct, "json") { return false }
return json.Valid(body)
} Prevention
- Check the HTTP status is 200 before unmarshaling the GitLab API body
- Verify Content-Type contains application/json before json.Unmarshal
- Do not point glow at private or authenticated GitLab projects - this path sends no token
When it happens
Trigger: The GitLab host returns HTML instead of JSON: a sign-in redirect from a private instance, a Cloudflare challenge page, a 429 rate-limit page, or pointing the URL at a host that is not actually a GitLab instance.
Common situations: Private/authenticated GitLab installations (glow's code path sends no token), Cloudflare-protected GitLab hosts, heavy API usage hitting rate limits, typos in the hostname that resolve to some other web server.
Understand the failure class
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- can't find README in GitLab repository
- can't find README in GitHub repository
- unable to parse json: %w
- unable to get url: %w
- unable to read http response body: %w
AI-assisted analysis of charmbracelet/glow@e3970c813d (2026-08-15).
Data as JSON: /api/errors/af1477a32e234781.
Report an issue: GitHub.