thanos-io/thanos · error

rule: unknown type field provided

Error message

rule: unknown type field provided %s; %v

What it means

Rule.UnmarshalJSON only accepts type values "recording" and "alerting" (case-insensitive). Any other non-empty value hits the default branch and errors with 'rule: unknown type field provided' including the given type and raw entry.

Solutions

  1. Correct the type field to "recording" or "alerting"
  2. Validate rules files against the expected schema before upload
  3. Check whether the producing tool uses a different rule schema and convert accordingly

Example fix

// before
{"type": "alert", "name": "HighErrors"}
// after
{"type": "alerting", "name": "HighErrors"}
Defensive patterns

Strategy: validation

Validate before calling

allowed := map[string]bool{"recording": true, "alerting": true}
var d struct { Type string `json:"type"` }
if json.Unmarshal(entry, &d) == nil && !allowed[strings.ToLower(d.Type)] {
    return fmt.Errorf("unsupported rule type %q", d.Type)
}

Type guard

func hasKnownRuleType(entry []byte) bool {
    var d struct { Type string `json:"type"` }
    if json.Unmarshal(entry, &d) != nil { return false }
    t := strings.ToLower(d.Type)
    return t == "recording" || t == "alerting"
}

Prevention

When it happens

Trigger: Decoding rule entries with a typo'd or unsupported type such as "alert", "record", "RecordingRule", or a type from a different tool's schema.

Common situations: Typos in hand-written rules; mixing rule schemas from other systems (Grafana, Mimir variants) into Cortex rules files; tooling writing its own type names.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — 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/21ac3ee831c331ce. Report an issue: GitHub.

Appendix: source

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

	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 "":
		return errors.Errorf("rule: no type field provided: %v", string(entry))
	default:
		return errors.Errorf("rule: unknown type field provided %s; %v", decider.Type, string(entry))
	}
	return nil
}

func (m *Rule) MarshalJSON() ([]byte, error) {
	if r := m.GetRecording(); r != nil {
		return json.Marshal(struct {
			*RecordingRule
			Type string `json:"type"`
		}{
			RecordingRule: r,
			Type:          RuleRecordingType,
		})
	}
	a := m.GetAlert()
	if a.Alerts == nil {
		// Ensure that empty slices are marshaled as '[]' and not 'null'.
		a.Alerts = make([]*AlertInstance, 0)

View on GitHub (pinned to 35b8b99117)