thanos-io/thanos · error

unmarshal alertmanager alert API response

Error message

unmarshal alertmanager alert API response

What it means

AlertmanagerAlerts fetches /api/v1/alerts from an Alertmanager and decodes the 2xx body into a struct with a data field of []*model.Alert. This error wraps json.Unmarshal failing because the body is not valid JSON or does not match that envelope (e.g. HTML page, wrong root type, data not an array).

Solutions

  1. Verify the base URL is the Alertmanager (default :9093), not Prometheus (:9090); log the raw body to confirm.
  2. Check for auth redirects/SSO proxies that return HTML login pages with status 200.
  3. Test directly with curl '<base>/api/v1/alerts' and confirm the JSON has a data array.
  4. Alertmanager API version: ensure the endpoint exists and is not an api/v2-only setup where shapes differ (this client targets the legacy api/v1).

Example fix

// before: wrong server
url: http://prometheus:9090
// after: Alertmanager serves /api/v1/alerts
url: http://alertmanager:9093
Defensive patterns

Strategy: try-catch

Validate before calling

// pre-flight: confirm this is Alertmanager's API
resp, _ := http.Get(base + "/api/v1/status/buildinfo") // AM returns 200 with version info; Prom buildinfo schema differs
// or simply check the configured port (9093 vs 9090) before constructing the client

Try / catch

alerts, err := client.AlertmanagerAlerts(ctx, u)
if err != nil && strings.Contains(err.Error(), "unmarshal alertmanager alert API response") {
    // wrong endpoint or HTML-200 portal page: log body/URL
    return err
}

Prevention

When it happens

Trigger: Client.AlertmanagerAlerts receives a 2xx response whose body fails to decode into {"data": [alert...]} — non-JSON body, data as object/string, or unexpected field types inside alerts.

Common situations: Pointing the client at the Prometheus API instead of Alertmanager (different response shape), an authentication portal returning an HTML 200 page, wrong port or path prefix, or a proxy intercepting the request.

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

Appendix: source

Thrown at pkg/promclient/promclient.go:653

	u.Path = path.Join(u.Path, "/api/v1/alerts")

	level.Debug(c.logger).Log("msg", "querying instant", "url", u.String())

	span, ctx := tracing.StartSpan(ctx, "/alertmanager_alerts HTTP[client]")
	defer span.Finish()

	body, _, err := c.req2xx(ctx, &u, http.MethodGet, nil)
	if err != nil {
		return nil, err
	}

	// Decode only ResultType and load Result only as RawJson since we don't know
	// structure of the Result yet.
	var v struct {
		Data []*model.Alert `json:"data"`
	}
	if err = json.Unmarshal(body, &v); err != nil {
		return nil, errors.Wrap(err, "unmarshal alertmanager alert API response")
	}
	sort.Slice(v.Data, func(i, j int) bool {
		return v.Data[i].Labels.Before(v.Data[j].Labels)
	})
	return v.Data, nil
}

// BuildVersion returns Prometheus version from /api/v1/status/buildinfo Prometheus endpoint.
// For Prometheus versions < 2.14.0 it returns "0" as Prometheus version.
func (c *Client) BuildVersion(ctx context.Context, base *url.URL) (string, error) {
	u := *base
	u.Path = path.Join(u.Path, "/api/v1/status/buildinfo")

	level.Debug(c.logger).Log("msg", "build version", "url", u.String())

	span, ctx := tracing.StartSpan(ctx, "/prom_buildversion HTTP[client]")
	defer span.Finish()

View on GitHub (pinned to 35b8b99117)