thanos-io/thanos · error

empty targetHealth

Error message

empty targetHealth

What it means

After successfully unquoting, TargetHealth.UnmarshalJSON rejects an empty string value because health must be one of the defined enum states (unknown, healthy, unhealthy). An empty targetHealth field in JSON triggers this error.

Solutions

  1. Provide a valid health value: "unknown", "healthy", or "unhealthy".
  2. If the source data may be empty, set the field to "unknown" before unmarshaling.
  3. Make the field a pointer (*TargetHealth) and skip unmarshal when absent instead of sending an empty string.
  4. Fix double-encoded values like "\"\"" to plain "unknown".

Example fix

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

Strategy: validation

Validate before calling

if health == "" { health = "unknown" }

Type guard

func validTargetHealth(s string) bool { return s == "unknown" || s == "healthy" || s == "unhealthy" }

Prevention

When it happens

Trigger: TargetHealth.UnmarshalJSON receives a JSON string that unquotes to "", e.g. "health": "" or "health": "\"\"" double-encoded.

Common situations: Empty DB/struct fields serialized to JSON by generic tooling; Prometheus targets data with missing health defaulted to empty string; hand-written fixtures.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

	}
}

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
}

func (x TargetHealth) Compare(y TargetHealth) int {
	return int(x) - int(y)
}

View on GitHub (pinned to 35b8b99117)