thanos-io/thanos · error

rule: type field unmarshal

Error message

rule: type field unmarshal: %v

What it means

Rule.UnmarshalJSON first peeks at the JSON 'type' field to decide whether the entry is a recording rule or an alert. If the raw bytes are not valid JSON at all, the json.Unmarshal error is wrapped as 'rule: type field unmarshal' including the offending entry. It means the rule entry is malformed JSON before type dispatch even happens.

Solutions

  1. Validate the rules file with jq or a JSON linter to find the malformed entry
  2. Fix quoting/escaping around the rule entry (especially when generated via templates or shell)
  3. Re-export or re-upload the rules from source

Example fix

// before (invalid JSON)
{"type": "alerting", "alert": "HighErrors", "expr": "rate(x[5m]) > 1,}
// after
{"type": "alerting", "alert": "HighErrors", "expr": "rate(x[5m]) > 1"}
Defensive patterns

Strategy: validation

Validate before calling

if !json.Valid(entry) {
    return fmt.Errorf("invalid rule JSON")
}

Try / catch

var m rulespb.Rule
if err := json.Unmarshal(entry, &m); err != nil {
    var serr *json.SyntaxError
    if errors.As(err, &serr) {
        log.Printf("JSON syntax error at offset %d: %v", serr.Offset, serr)
    }
    return err
}

Prevention

When it happens

Trigger: Feeding Rule entries from a rules file, API payload, or object store that contain invalid JSON (truncated file, trailing commas, concatenated JSON objects).

Common situations: Corrupted or partially written rules files in config storage; copying rules between systems with quoting damage; templating tools emitting invalid JSON.

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 thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/0e4227086476fca7. Report an issue: GitHub.

Appendix: source

Thrown at pkg/rules/rulespb/custom.go:213

	return strings.Compare(r1.Key(), r2.Key())
}

// Key returns the group key similar resembling Prometheus logic.
// See https://github.com/prometheus/prometheus/blob/869f1bc587e667b79721852d5badd9f70a39fc3f/rules/manager.go#L1062-L1065
func (r *RuleGroup) Key() string {
	if r == nil {
		return ""
	}

	return r.File + ";" + r.Name
}

func (m *Rule) UnmarshalJSON(entry []byte) error {
	decider := struct {
		Type string `json:"type"`
	}{}
	if err := json.Unmarshal(entry, &decider); err != nil {
		return errors.Wrapf(err, "rule: type field unmarshal: %v", string(entry))
	}

	switch strings.ToLower(decider.Type) {
	case "recording":
		r := &RecordingRule{}
		if err := json.Unmarshal(entry, r); err != nil {
			return errors.Wrapf(err, "rule: recording rule unmarshal: %v", string(entry))
		}

		m.Result = &Rule_Recording{Recording: r}
	case "alerting":
		r := &Alert{}
		if err := json.Unmarshal(entry, r); err != nil {
			return errors.Wrapf(err, "rule: alerting rule unmarshal: %v", string(entry))
		}

		m.Result = &Rule_Alert{Alert: r}
	case "":

View on GitHub (pinned to 35b8b99117)