thanos-io/thanos · error

targetHealth: unquote

Error message

targetHealth: unquote %v

What it means

TargetHealth is a custom enum type that unmarshals from a JSON string. This error wraps strconv.Unquote failing, meaning the JSON value for targetHealth was not a properly quoted string (or was malformed JSON text).

Solutions

  1. Fix the JSON input so targetHealth is a quoted string, e.g. "health": "healthy" instead of health: healthy.
  2. Ensure producers use TargetHealth.MarshalJSON instead of serializing the enum as an int.
  3. If parsing third-party JSON, preprocess/normalize the field to a quoted string before unmarshal.
  4. Check the value reported in the error message (entry) to see the exact malformed token.

Example fix

// before
{"health": healthy}
// after
{"health": "healthy"}
Defensive patterns

Strategy: validation

Validate before calling

let health = data.health;
if (typeof health !== 'string') throw new Error('targetHealth must be a JSON string');

Type guard

function isQuotedJSONString(v string) bool { return len(v) >= 2 && v[0] == '"' && v[len(v)-1] == '"' }

Try / catch

var h targetspb.TargetHealth
if err := json.Unmarshal(raw, &h); err != nil {
    return fmt.Errorf("bad targetHealth %q: %w", raw, err)
}

Prevention

When it happens

Trigger: TargetHealth.UnmarshalJSON receives bytes that are not a valid JSON string literal, so strconv.Unquote fails — e.g. unquoted value like healthy or a number.

Common situations: Hand-edited or generated JSON where health is emitted unquoted; a client marshaling TargetHealth as a raw number; custom tooling writing struct fields directly to JSON.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/99f5c4f069aeafc9. Report an issue: GitHub.

Appendix: source

Thrown at pkg/targets/targetspb/custom.go:35

	return &TargetsResponse{
		Result: &TargetsResponse_Targets{
			Targets: targets,
		},
	}
}

func NewWarningTargetsResponse(warning error) *TargetsResponse {
	return &TargetsResponse{
		Result: &TargetsResponse_Warning{
			Warning: warning.Error(),
		},
	}
}

func (x *TargetHealth) UnmarshalJSON(entry []byte) error {
	fieldStr, err := strconv.Unquote(string(entry))
	if err != nil {
		return errors.Wrapf(err, "targetHealth: unquote %v", string(entry))
	}

	if fieldStr == "" {
		return errors.New("empty targetHealth")
	}

	state, ok := TargetHealth_value[strings.ToUpper(fieldStr)]
	if !ok {
		return errors.Errorf("unknown targetHealth: %v", string(entry))
	}
	*x = TargetHealth(state)
	return nil
}

func (x *TargetHealth) MarshalJSON() ([]byte, error) {
	return []byte(strconv.Quote(strings.ToLower(x.String()))), nil
}

View on GitHub (pinned to 35b8b99117)