Netflix/chaosmonkey · error

unexpected response code (%d) from %s

Error message

unexpected response code (%d) from %s

What it means

After a successful GET, Spinnaker.Get expects HTTP 200. Any other status code yields 'unexpected response code (%d) from %s'. This signals the gate responded but rejected or redirected the request rather than returning the app config payload.

Source

Thrown at spinnaker/config.go:43

// Get implements chaosmonkey.Getter.Get
func (s Spinnaker) Get(app string) (c *chaosmonkey.AppConfig, err error) {
	// avoid expanding the response to avoid unneeded load
	url := s.appURL(app) + "?expand=false"
	resp, err := s.client.Get(url)
	if err != nil {
		return nil, errors.Wrapf(err, "http get failed at %s", url)
	}

	defer func() {
		if cerr := resp.Body.Close(); cerr != nil && err == nil {
			err = errors.Wrapf(err, "body close failed at %s", url)
		}
	}()

	// should return a 200
	if resp.StatusCode != http.StatusOK {
		return nil, errors.Errorf("unexpected response code (%d) from %s", resp.StatusCode, url)
	}

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Wrapf(err, "body read failed at %s", url)
	}

	return fromJSON(body)
}

View on GitHub (pinned to eaa28fb761)

Solutions

  1. Check the reported status code: 404 means verify the app exists in Spinnaker; 401/403 means fix credentials/auth for the gate
  2. Confirm the app name used in appURL matches Spinnaker's application name exactly
  3. Inspect gate/proxy logs for 5xx causes (upstream Spinnaker health)
  4. Ensure the HTTP client follows redirects if a 3xx is returned

Example fix

// caller-side guard
resp, err := http.Get(gateURL + "/applications/myapp?expand=false")
if err != nil { return err }
if resp.StatusCode == http.StatusNotFound {
    log.Println("app not found in spinnaker; skipping")
    return nil
}
if resp.StatusCode != http.StatusOK {
    return fmt.Errorf("gate returned %d for %s", resp.StatusCode, gateURL)
}
Defensive patterns

Strategy: type-guard

Validate before calling

resp, err := http.Get(gateURL + "/applications/" + app + "?expand=false")
if err != nil { return err }
switch {
case resp.StatusCode == http.StatusNotFound:
    return fmt.Errorf("app %s does not exist in Spinnaker", app)
case resp.StatusCode == http.StatusUnauthorized || resp.StatusCode == http.StatusForbidden:
    return errors.New("invalid or expired Spinnaker credentials")
case resp.StatusCode != http.StatusOK:
    return fmt.Errorf("gate returned %d", resp.StatusCode)
}

Type guard

func isNotFoundStatus(err error) bool {
    m := regexp.MustCompile(`unexpected response code \((\d+)\)`).FindStringSubmatch(err.Error())
    return m != nil && m[1] == "404"
}

Try / catch

cfg, err := getter.Get(app)
if err != nil {
    if isNotFoundStatus(err) {
        log.Printf("skipping %s: not in Spinnaker", app)
        return nil // treat as non-fatal
    }
    return err
}

Prevention

When it happens

Trigger: Spinnaker returns 404 (unknown app), 401/403 (missing/invalid auth), 5xx (gate internal error), or 3xx that the client did not follow for GET <appURL>?expand=false.

Common situations: Querying an app name that does not exist in Spinnaker; auth token/account credentials missing or expired; Spinnaker gate overloaded returning 502/503 from a proxy; app deleted after the monkey listed it.

Related errors


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