{"record":{"id":"0312690ca87dc0d2","repo":"glanceapp/glance","slug":"d-s","errorCode":null,"errorMessage":"%d %s","messagePattern":"%d %s","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/glance/widget-custom-api.go","lineNumber":268,"sourceCode":"\tbodyBytes, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbody := strings.TrimSpace(string(bodyBytes))\n\n\tif !req.SkipJSONValidation && body != \"\" && !gjson.Valid(body) {\n\t\tif 200 <= resp.StatusCode && resp.StatusCode < 300 {\n\t\t\ttruncatedBody, isTruncated := limitStringLength(body, 100)\n\t\t\tif isTruncated {\n\t\t\t\ttruncatedBody += \"... <truncated>\"\n\t\t\t}\n\n\t\t\tslog.Error(\"Invalid response JSON in custom API widget\", \"url\", req.httpRequest.URL.String(), \"body\", truncatedBody)\n\t\t\treturn nil, errors.New(\"invalid response JSON\")\n\t\t}\n\n\t\treturn nil, fmt.Errorf(\"%d %s\", resp.StatusCode, http.StatusText(resp.StatusCode))\n\n\t}\n\n\treturn &customAPIResponseData{\n\t\tJSON:     decoratedGJSONResult{gjson.Parse(body)},\n\t\tResponse: resp,\n\t}, nil\n}\n\nfunc fetchAndRenderCustomAPIRequest(\n\tprimaryReq *CustomAPIRequest,\n\tsubReqs map[string]*CustomAPIRequest,\n\toptions customAPIOptions,\n\ttmpl *template.Template,\n) (template.HTML, error) {\n\tvar primaryData *customAPIResponseData\n\tsubData := make(map[string]*customAPIResponseData, len(subReqs))\n\tvar err error","sourceCodeStart":250,"sourceCodeEnd":286,"githubUrl":"https://github.com/glanceapp/glance/blob/91324e8de762702e97b0ac5c8e36271d644d8642/internal/glance/widget-custom-api.go#L250-L286","documentation":"Returned by the custom API response handler when the HTTP status is outside 200-299 and the body is not valid JSON: fmt.Errorf(\"%d %s\", resp.StatusCode, http.StatusText(resp.StatusCode)) yields messages like \"502 Bad Gateway\" or \"401 Unauthorized\". On 2xx statuses invalid JSON produces a different error (\"invalid response JSON\"), so this format specifically means non-2xx + non-JSON body.","triggerScenarios":"Any custom-api request (primary or subrequest) whose endpoint returns e.g. 502/503 from a reverse proxy HTML error page, 401/403 from an auth wall returning HTML, or 404 for a wrong URL path.","commonSituations":"Expired API key so the gateway returns an HTML login page; wrong URL or path segment; upstream service down and the proxy's error page is HTML; Cloudflare challenge pages.","solutions":["curl the exact URL with the same headers from the widget config and inspect the status code and body.","Fix auth: add/refresh the API key or token header the endpoint expects.","Correct the URL/path (404) or scheme/host (502).","If the endpoint legitimately returns non-JSON error bodies on failure, consider options that tolerate it (e.g. skip JSON validation) or accept the widget showing the error status."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"data, err := executeCustomAPIRequest(req)\nif err != nil {\n    if strings.Contains(err.Error(), \"invalid response JSON\") {\n        // 2xx but bad JSON: log body sample, likely API contract change\n    } else {\n        // \"NNN Status Text\": HTTP-level failure — auth, proxy, or wrong URL\n    }\n    return nil, err\n}","preventionTips":["Verify endpoints with curl (same URL + headers) before adding them to config.","Keep API keys current; expired keys commonly produce HTML auth pages.","Prefer JSON-returning endpoints; HTML error pages from proxies are the usual trigger."],"tags":["http","network","custom-api","auth"],"backgroundTag":null,"analyzedSha":"91324e8de762702e97b0ac5c8e36271d644d8642","analyzedAt":"2026-08-15T14:12:54.279Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}