{"record":{"id":"fb7048efbce0a29b","repo":"glanceapp/glance","slug":"unexpected-status-code-d-for-s-response-s","errorCode":null,"errorMessage":"unexpected status code %d for %s, response: %s","messagePattern":"unexpected status code (.+?) for (.+?), response: (.+?)","errorType":"http","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/glance/widget-utils.go","lineNumber":119,"sourceCode":"// TODO: tidy up, these are a copy of the above but with a line changed\nfunc decodeXmlFromRequest[T any](client requestDoer, request *http.Request) (T, error) {\n\tvar result T\n\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\tdefer response.Body.Close()\n\n\tbody, err := io.ReadAll(response.Body)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\tif response.StatusCode != http.StatusOK {\n\t\ttruncatedBody, _ := limitStringLength(string(body), 256)\n\n\t\treturn result, fmt.Errorf(\n\t\t\t\"unexpected status code %d for %s, response: %s\",\n\t\t\tresponse.StatusCode,\n\t\t\trequest.URL,\n\t\t\ttruncatedBody,\n\t\t)\n\t}\n\n\terr = xml.Unmarshal(body, &result)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\treturn result, nil\n}\n\nfunc decodeXmlFromRequestTask[T any](client requestDoer) func(*http.Request) (T, error) {\n\treturn func(request *http.Request) (T, error) {\n\t\treturn decodeXmlFromRequest[T](client, request)","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/glanceapp/glance/blob/91324e8de762702e97b0ac5c8e36271d644d8642/internal/glance/widget-utils.go#L101-L137","documentation":"The XML twin of the JSON helper (decodeXmlFromRequest): status was not 200, and the error includes the status code, URL, and a 256-byte body snippet. Used for RSS/Atom-style feeds such as the videos widget's YouTube feeds.","triggerScenarios":"A feed URL returning non-200: 404 for a deleted channel/playlist ID, 429 from YouTube feed throttling, 403 from an IP block, or 5xx from the feed host.","commonSituations":"A YouTube channel ID in the videos config is wrong or the channel was removed; a self-hosted RSS host blocks the server's IP; too frequent polling of the same feeds.","solutions":["Inspect the status code and body snippet: 404 means a bad/deleted channel or playlist ID, 429 means back off","Correct the channel/playlist IDs in the videos widget configuration","Increase the widget's cache time so feeds are fetched less often"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":"func isUnexpectedStatus(err error) bool {\n\treturn err != nil && strings.Contains(err.Error(), \"unexpected status code\")\n}","tryCatchPattern":"feed, err := decodeXmlFromRequest[ T ](client, req)\nif err != nil && isUnexpectedStatus(err) {\n    // 404: drop the bad feed from config; 429/5xx: retry with backoff\n    slog.Warn(\"feed fetch failed\", \"url\", req.URL.String(), \"err\", err)\n}","preventionTips":["Verify channel/playlist IDs resolve before adding them to the videos widget","Cache feed results to keep burst polling under throttling thresholds","Ensure the Glance host's IP is not blocked by the feed provider"],"tags":["http","network","xml","feed","youtube"],"backgroundTag":null,"analyzedSha":"91324e8de762702e97b0ac5c8e36271d644d8642","analyzedAt":"2026-08-15T14:12:54.279Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}