thanos-io/thanos · error

unmarshaling scalar value from

Error message

unmarshaling scalar value from %v

What it means

The second element of the scalar tuple must decode into model.SampleValue (a float64). This error wraps json.Unmarshal failing on resultPointSlice[1] — the value element is not a number-compatible JSON value (e.g. a non-numeric string, null, or an object).

Solutions

  1. Ensure the value element is a numeric JSON value or a string that parses as float (Prometheus emits values as strings like "42"; model.SampleValue accepts those).
  2. Log resultPointSlice[1] to inspect the actual payload.
  3. Fix the emitting server/fixture to return Prometheus-style numeric strings.
  4. Replace null placeholders with numeric values (0) if the source cannot produce a real sample.

Example fix

// before
"result": [1710000000000, "n/a"]
// after
"result": [1710000000000, "42"]
Defensive patterns

Strategy: validation

Validate before calling

// fixture check: tuple[1] must parse as float
var val model.SampleValue
if err := json.Unmarshal(raw[1], &val); err != nil {
    return fmt.Errorf("scalar value must be numeric, got %s", raw[1])
}

Try / catch

if err != nil && strings.Contains(err.Error(), "unmarshaling scalar value") {
    // value element wrong type; log raw payload and fix emitter
}

Prevention

When it happens

Trigger: QueryInstant/TestRule_UnmarshalScalarResponse receives a scalar result whose tuple[1] cannot parse as a float: strings like "NaN" outside quotes handling, "n/a", units embedded in values, or null.

Common situations: Nonstandard backends returning formatted values ("1.2k", "12%"), null for missing data, or test fixtures with wrong value types.

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

Appendix: source

Thrown at pkg/promclient/promclient.go:624

func convertScalarJSONToVector(scalarJSONResult json.RawMessage) (model.Vector, error) {
	var (
		// Do not specify exact length of the expected slice since JSON unmarshaling
		// would make the length fit the size and we won't be able to check the length afterwards.
		resultPointSlice []json.RawMessage
		resultTime       model.Time
		resultValue      model.SampleValue
	)
	if err := json.Unmarshal(scalarJSONResult, &resultPointSlice); err != nil {
		return nil, err
	}
	if len(resultPointSlice) != 2 {
		return nil, errors.Errorf("invalid scalar result format %v, expected timestamp -> value tuple", resultPointSlice)
	}
	if err := json.Unmarshal(resultPointSlice[0], &resultTime); err != nil {
		return nil, errors.Wrapf(err, "unmarshaling scalar time from %v", resultPointSlice)
	}
	if err := json.Unmarshal(resultPointSlice[1], &resultValue); err != nil {
		return nil, errors.Wrapf(err, "unmarshaling scalar value from %v", resultPointSlice)
	}
	return model.Vector{&model.Sample{
		Metric:    model.Metric{},
		Value:     resultValue,
		Timestamp: resultTime}}, nil
}

// AlertmanagerAlerts returns alerts from Alertmanager.
func (c *Client) AlertmanagerAlerts(ctx context.Context, base *url.URL) ([]*model.Alert, error) {
	u := *base
	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)

View on GitHub (pinned to 35b8b99117)