thanos-io/thanos · error

error marshaling response

Error message

error marshaling response

What it means

API.Respond marshals the response struct with jsoniter (stdlib-compatible). If marshaling fails, it logs 'error marshaling response' and returns HTTP 500 with the marshal error text. This is an internal encoding failure, not a client input problem.

Solutions

  1. Check the logged err field to find which field fails to marshal.
  2. Sanitize numeric results: replace NaN/Inf with null before responding.
  3. Fix the handler's return type so it contains only JSON-encodable values.

Example fix

// before
return promql.Vector{{Point: promql.Point{T: ts, V: math.NaN()}}}, nil
// after
v := math.NaN()
if math.IsNaN(v) { v = 0 } // or encode as null
return promql.Vector{{Point: promql.Point{T: ts, V: v}}}, nil
Defensive patterns

Strategy: fallback

Try / catch

resp = sanitizeForJSON(resp) // replace NaN/Inf with nil
b, err := json.Marshal(resp)
if err != nil {
    level.Error(logger).Log("msg", "error marshaling response", "err", err)
    http.Error(w, err.Error(), http.StatusInternalServerError)
    return
}

Prevention

When it happens

Trigger: json.Marshal(resp) returns an error — e.g. resp.Data contains a value JSON cannot encode (NaN/Inf floats from query results, a channel/func field, or a custom MarshalJSON returning an error).

Common situations: Query endpoints returning NaN/Inf values in series data; a handler returning unencodable types (e.g. unmarshalable time formats or cyclic structures) in a custom API extension.

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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

Appendix: source

Thrown at pkg/api/api.go:287

	w.Header().Set("Content-Type", "application/json")
	if shouldNotCacheBecauseOfWarnings(warnings) {
		w.Header().Set("Cache-Control", "no-store")
	}
	w.WriteHeader(http.StatusOK)

	resp := &response{
		Status: StatusSuccess,
		Data:   data,
	}
	for _, warn := range warnings {
		resp.Warnings = append(resp.Warnings, warn.Error())
	}

	json := jsoniter.ConfigCompatibleWithStandardLibrary
	b, err := json.Marshal(resp)
	if err != nil {
		level.Error(logger).Log("msg", "error marshaling response", "err", err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	if n, err := w.Write(b); err != nil {
		level.Error(logger).Log("msg", "error writing response", "bytesWritten", n, "err", err)
	}
}

func RespondError(w http.ResponseWriter, apiErr *ApiError, data any, logger log.Logger) {
	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Cache-Control", "no-store")

	var code int
	switch apiErr.Typ {
	case ErrorBadData:
		code = http.StatusBadRequest
	case ErrorExec:
		code = 422

View on GitHub (pinned to 35b8b99117)