{"record":{"id":"6fabba6adc785d0c","repo":"Netflix/chaosmonkey","slug":"could-not-parse-spinnaker-apps-list-from-s-body","errorCode":null,"errorMessage":"could not parse spinnaker apps list from %s: body: \"%s\": %v","messagePattern":"could not parse spinnaker apps list from (.+?): body: \"(.+?)\": (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"spinnaker/spinnaker.go","lineNumber":366,"sourceCode":"\tresp, err := s.client.Get(url)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not retrieve list of apps from spinnaker url %s: %v\", url, err)\n\t}\n\n\tdefer func() {\n\t\tif cerr := resp.Body.Close(); cerr != nil && err == nil {\n\t\t\terr = fmt.Errorf(\"failed to close response body from %s: %v\", url, err)\n\t\t}\n\t}()\n\n\tbody, err := ioutil.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to read body when retrieving spinnaker app names from %s: %v\", url, err)\n\t}\n\tvar apps []spinnakerApp\n\terr = json.Unmarshal(body, &apps)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not parse spinnaker apps list from %s: body: \\\"%s\\\": %v\", url, string(body), err)\n\t}\n\n\tresult := make([]string, len(apps))\n\tfor i, app := range apps {\n\t\tresult[i] = app.Name\n\t}\n\n\treturn result, nil\n\n}\n\n// spinnakerApp returns an app as represented by the Spinnaker API\ntype spinnakerApp struct {\n\tName string\n}\n\n// clusters returns a map from account name to list of cluster names\nfunc (s Spinnaker) clusters(appName string) spinnakerClusters {","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/Netflix/chaosmonkey/blob/eaa28fb761c0ebe8644d1333e5d164e9cc3071e9/spinnaker/spinnaker.go#L348-L384","documentation":"AppNames() unmarshals the response body into []spinnakerApp; if the body is not valid JSON or doesn't match the expected array shape, json.Unmarshal fails and the error is wrapped with the URL and the offending body text for inspection.","triggerScenarios":"Calling AppNames() when the Spinnaker apps endpoint returns a 200 response whose body fails json.Unmarshal into a JSON array of objects with a name field — e.g. HTML error pages, empty bodies, or a differently-shaped JSON object.","commonSituations":"A proxy/LB or login page returning HTML instead of the API response; wrong URL path hitting a non-API route; Spinnaker API version change altering the response shape; TLS-accepted-but-unauthorized responses returning non-JSON errors with 200.","solutions":["Inspect the body text embedded in the error to see what was actually returned","curl the apps URL with the client cert and confirm it returns a JSON array of apps","Verify the endpoint URL includes the correct API path/version","Check for proxies or auth redirects that could substitute HTML for the JSON payload"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Validate the apps endpoint returns JSON before parsing:\nresp, err := http.Get(appsURL)\nif err != nil { return err }\nct := resp.Header.Get(\"Content-Type\")\nif !strings.Contains(ct, \"application/json\") {\n\treturn fmt.Errorf(\"apps endpoint returned non-JSON content type %q\", ct)\n}","typeGuard":null,"tryCatchPattern":"names, err := spin.AppNames()\nif err != nil {\n\tif strings.Contains(err.Error(), \"could not parse spinnaker apps list\") {\n\t\tlog.Printf(\"unexpected Spinnaker response; check URL/auth/proxies: %v\", err)\n\t\treturn err\n\t}\n\treturn err\n}","preventionTips":["Verify the apps URL returns a JSON array with curl before deploying","Check for proxies/LBs that substitute HTML (login/error pages)","Confirm API path/version after Spinnaker upgrades","Ensure auth failures aren't returning 200 with non-JSON bodies"],"tags":["json","parsing","spinnaker"],"backgroundTag":"json-unmarshal-failed","analyzedSha":"eaa28fb761c0ebe8644d1333e5d164e9cc3071e9","analyzedAt":"2026-09-03T17:04:39.020Z","contentChangedAt":"2026-09-03T17:04:39.020Z","schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}