{"record":{"id":"88bd61596d9a9cea","repo":"router-for-me/CLIProxyAPI","slug":"home-models-payload-is-empty","errorCode":null,"errorMessage":"home models payload is empty","messagePattern":"home models payload is empty","errorType":"http","errorClass":null,"httpStatus":502,"severity":"error","filePath":"internal/api/server_routes.go","lineNumber":970,"sourceCode":"\t\treturn msg\n\t}\n\treturn \"home models request failed\"\n}\n\nfunc unmarshalHomeModelsTopLevel(raw []byte) (map[string]json.RawMessage, bool) {\n\tif len(raw) == 0 {\n\t\treturn nil, false\n\t}\n\tvar top map[string]json.RawMessage\n\tif errUnmarshal := json.Unmarshal(raw, &top); errUnmarshal != nil {\n\t\treturn nil, false\n\t}\n\treturn top, true\n}\n\nfunc decodeHomeModels(raw []byte) ([]homeModelEntry, error) {\n\tif len(raw) == 0 {\n\t\treturn nil, fmt.Errorf(\"home models payload is empty\")\n\t}\n\n\tvar bySection map[string][]map[string]any\n\tif err := json.Unmarshal(raw, &bySection); err != nil {\n\t\treturn nil, fmt.Errorf(\"parse home models payload: %w\", err)\n\t}\n\tif len(bySection) == 0 {\n\t\treturn nil, fmt.Errorf(\"home models payload has no sections\")\n\t}\n\n\tseen := make(map[string]struct{})\n\tout := make([]homeModelEntry, 0, 256)\n\tfor _, models := range bySection {\n\t\tfor _, model := range models {\n\t\t\tid, _ := model[\"id\"].(string)\n\t\t\tid = strings.TrimSpace(id)\n\t\t\tif id == \"\" {\n\t\t\t\tname, _ := model[\"name\"].(string)","sourceCodeStart":952,"sourceCodeEnd":988,"githubUrl":"https://github.com/router-for-me/CLIProxyAPI/blob/78f0c4079e3e6273d65d03b5549cffc898703264/internal/api/server_routes.go#L952-L988","documentation":"decodeHomeModels rejects its input when the raw payload byte slice is empty (len == 0). This function parses the JSON payload fetched from the home models source into model entries; an empty body means nothing was fetched or an upstream returned zero bytes.","triggerScenarios":"The upstream home models endpoint returned HTTP 200 with an empty body; a fetch layer returned a nil/empty byte slice without an error; the cached payload was truncated to zero length.","commonSituations":"Upstream API change returning 204 instead of 200 with content; a caching layer storing an empty value on a failed fetch; network intermediary stripping the body.","solutions":["Verify what the home models fetch actually returned (log status code and Content-Length before decoding)","Treat an empty payload as a fetch failure and retry or fall back to the last known-good cached payload instead of decoding empty bytes","If the upstream now legitimately returns empty, skip the decode path rather than treating it as a model list"],"exampleFix":"// before\nraw, _ := fetchHomeModels(ctx)\nentries, err := decodeHomeModels(raw)\n\n// after\nraw, errFetch := fetchHomeModels(ctx)\nif errFetch != nil || len(raw) == 0 {\n\treturn nil, fmt.Errorf(\"home models fetch returned no data: %w\", errFetch)\n}\nentries, err := decodeHomeModels(raw)","handlingStrategy":"validation","validationCode":"if len(raw) == 0 {\n\treturn fmt.Errorf(\"home models fetch returned empty payload\")\n}\nentries, err := decodeHomeModels(raw)","typeGuard":null,"tryCatchPattern":"if _, err := decodeHomeModels(raw); err != nil {\n\tlog.Warnf(\"home models decode failed: %v; keeping previous model list\", err)\n}","preventionTips":["Validate Content-Length/Content-Type on the models fetch before caching","Never cache an empty payload as a successful fetch","Keep the last known-good payload for fallback"],"tags":["json","models-list","parsing","upstream"],"backgroundTag":null,"analyzedSha":"78f0c4079e3e6273d65d03b5549cffc898703264","analyzedAt":"2026-08-15T12:26:37.444Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}