Netflix/chaosmonkey · error

json unmarshal failed

Error message

json unmarshal failed

What it means

fromJSON unmarshals the raw Spinnaker response into an internal parsedJSON structure. If json.Unmarshal fails, the error is wrapped as 'json unmarshal failed'. The response was readable HTTP 200 but its JSON shape/types do not match the expected struct.

Source

Thrown at spinnaker/fromjson.go:98

//	 	  	"stack": "foo",
//	 	  	"detail": "bar"
//	 	  	}
//	 	  ],
//	 	  "whitelist": [
//	 	  	{
//	 	  	"account": "test",
//	 	  	"stack": "*",
//	 	  	"region": "*",
//	 	  	"detail": "*"
//	 	  	}
//	 	  ]
//		  }
func fromJSON(js []byte) (*chaosmonkey.AppConfig, error) {
	parsed := new(parsedJSON)
	err := json.Unmarshal(js, parsed)

	if err != nil {
		return nil, errors.Wrap(err, "json unmarshal failed")
	}

	if parsed.Attributes == nil {
		return nil, errors.New("'attributes' field missing")
	}

	if parsed.Attributes.ChaosMonkey == nil {
		return nil, errors.New("'attributes.chaosMonkey' field missing")
	}

	cm := parsed.Attributes.ChaosMonkey

	if cm.Enabled == nil {
		return nil, errors.New("'attributes.chaosMonkey.enabled' field missing")
	}

	// Check if mean time between kills is missing.
	// If not enabled, it's ok if it's missing

View on GitHub (pinned to eaa28fb761)

Solutions

  1. Log/dump the raw response body (or its first bytes) to see what was actually received
  2. Confirm the endpoint returns the expected Spinnaker application JSON (compare against a curl of the gate URL)
  3. Check for proxies/auth layers returning HTML with 200 status
  4. Verify the Spinnaker API version is compatible with this library's parsedJSON schema
  5. Add a content-type check before unmarshaling

Example fix

// caller-side guard before trusting Get output
body, _ := ioutil.ReadAll(resp.Body)
if !json.Valid(body) {
    log.Printf("non-JSON response from gate: %.200s", body)
    return errors.New("gate returned invalid JSON")
}
Defensive patterns

Strategy: validation

Validate before calling

body, err := ioutil.ReadAll(resp.Body)
if err != nil { return err }
if len(bytes.TrimSpace(body)) == 0 {
    return errors.New("empty response body from gate")
}
if !json.Valid(body) {
    return fmt.Errorf("non-JSON response from gate: %.200s", body)
}

Type guard

func looksLikeGateJSON(body []byte) bool {
    if !json.Valid(body) { return false }
    var probe struct {
        Attributes map[string]interface{} `json:"attributes"`
    }
    return json.Unmarshal(body, &probe) == nil && probe.Attributes != nil
}

Try / catch

cfg, err := getter.Get(app)
if err != nil && strings.Contains(err.Error(), "json unmarshal failed") {
    log.Printf("gate returned malformed JSON for %s; check proxies/API version: %+v", app, err)
    return err
}

Prevention

When it happens

Trigger: json.Unmarshal(js, parsed) returns an error: response is empty, HTML (e.g. an error page from a proxy), truncated JSON, or field types differing from parsedJSON expectations (e.g. a string where a number is expected).

Common situations: A reverse proxy or login page returning HTML with status 200; Spinnaker API version changes altering the response schema; interrupted responses producing truncated JSON; empty body from gate on odd conditions.

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/2102e7bb26129b34. Report an issue: GitHub.