thanos-io/thanos · error

unmarshaling scalar time from

Error message

unmarshaling scalar time from %v

What it means

The first element of the scalar tuple must decode into prometheus/common's model.Time (a millisecond Unix timestamp number). This error wraps json.Unmarshal failing on resultPointSlice[0] — the timestamp element is not a number (e.g. a string, null, or object).

Solutions

  1. Ensure the server emits the timestamp as a JSON number in Unix milliseconds.
  2. Log resultPointSlice[0] to see the offending value.
  3. Fix test fixtures to use numeric epoch-millisecond timestamps.
  4. If the upstream genuinely sends string timestamps, convert on the server side rather than patching the client.

Example fix

// before
"result": ["2024-03-09T12:00:00Z", "42"]
// after
"result": [1710000000000, "42"]
Defensive patterns

Strategy: validation

Validate before calling

// fixture check: tuple[0] must be a JSON number (unix ms)
var ts json.Number
if err := json.Unmarshal(raw[0], &ts); err != nil {
    return fmt.Errorf("scalar timestamp must be numeric unix ms, got %s", raw[0])
}

Try / catch

if err != nil && strings.Contains(err.Error(), "unmarshaling scalar time") {
    // timestamp element wrong type; log it and fix the source
}

Prevention

When it happens

Trigger: QueryInstant/TestRule_UnmarshalScalarResponse receives a scalar result whose tuple[0] is not JSON-parseable into model.Time: string timestamps, null, or nested values.

Common situations: Custom or buggy server emitting timestamps as ISO strings instead of epoch milliseconds; hand-written test fixtures with wrong types; middleware converting numbers to strings.

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

Appendix: source

Thrown at pkg/promclient/promclient.go:621

// Scalar response consists of array with mixed types so it needs to be
// unmarshaled separately.
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]")

View on GitHub (pinned to 35b8b99117)