Netflix/chaosmonkey · error

could not parse spinnaker apps list from %s: body: "%s": %v

Error message

could not parse spinnaker apps list from %s: body: "%s": %v

What it means

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.

Source

Thrown at spinnaker/spinnaker.go:366

	resp, err := s.client.Get(url)
	if err != nil {
		return nil, fmt.Errorf("could not retrieve list of apps from spinnaker url %s: %v", url, err)
	}

	defer func() {
		if cerr := resp.Body.Close(); cerr != nil && err == nil {
			err = fmt.Errorf("failed to close response body from %s: %v", url, err)
		}
	}()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, fmt.Errorf("failed to read body when retrieving spinnaker app names from %s: %v", url, err)
	}
	var apps []spinnakerApp
	err = json.Unmarshal(body, &apps)
	if err != nil {
		return nil, fmt.Errorf("could not parse spinnaker apps list from %s: body: \"%s\": %v", url, string(body), err)
	}

	result := make([]string, len(apps))
	for i, app := range apps {
		result[i] = app.Name
	}

	return result, nil

}

// spinnakerApp returns an app as represented by the Spinnaker API
type spinnakerApp struct {
	Name string
}

// clusters returns a map from account name to list of cluster names
func (s Spinnaker) clusters(appName string) spinnakerClusters {

View on GitHub (pinned to eaa28fb761)

Solutions

  1. Inspect the body text embedded in the error to see what was actually returned
  2. curl the apps URL with the client cert and confirm it returns a JSON array of apps
  3. Verify the endpoint URL includes the correct API path/version
  4. Check for proxies or auth redirects that could substitute HTML for the JSON payload
Defensive patterns

Strategy: validation

Validate before calling

// Validate the apps endpoint returns JSON before parsing:
resp, err := http.Get(appsURL)
if err != nil { return err }
ct := resp.Header.Get("Content-Type")
if !strings.Contains(ct, "application/json") {
	return fmt.Errorf("apps endpoint returned non-JSON content type %q", ct)
}

Try / catch

names, err := spin.AppNames()
if err != nil {
	if strings.Contains(err.Error(), "could not parse spinnaker apps list") {
		log.Printf("unexpected Spinnaker response; check URL/auth/proxies: %v", err)
		return err
	}
	return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of Netflix/chaosmonkey@eaa28fb761 (2026-09-03). Data as JSON: /api/errors/6fabba6adc785d0c. Report an issue: GitHub.