thanos-io/thanos · error

Invalid time value for

Error message

Invalid time value for '%s'

What it means

This error is produced when a time parameter (e.g. 'start', 'end', 'time') on query/metadata endpoints cannot be parsed by parseTime, and is wrapped with the parameter name via errors.Wrapf. parseTime accepts unix floats (seconds, fractional seconds allowed) and RFC3339Nano strings; anything else fails, surfacing ErrorBadData (HTTP 400).

Solutions

  1. Send a unix timestamp in seconds (float ok) or an RFC3339Nano string like 2024-01-02T15:04:05Z.
  2. Encode the value in the URL (RFC3339 '+' must be %2B in query strings).
  3. Convert 'now' to an explicit timestamp client-side before calling.
  4. Use milliseconds correctly: divide epoch-millis by 1000 for seconds.

Example fix

// before
curl 'http://query:9090/api/v1/query?query=up&time=2024-01-02 15:04:05'
// after
curl 'http://query:9090/api/v1/query?query=up&time=2024-01-02T15%3A04%3A05Z'
Defensive patterns

Strategy: validation

Validate before calling

function toApiTime(v) {
  if (v instanceof Date) return v.toISOString();
  const n = Number(v);
  if (Number.isFinite(n)) return String(n); // unix seconds (float ok)
  if (!Number.isNaN(Date.parse(v))) return new Date(v).toISOString();
  throw new Error(`invalid time: ${v}`);
}

Try / catch

try { const t = toApiTime(raw); } catch (e) { /* fix or drop the param before calling API */ }

Prevention

When it happens

Trigger: GET /api/v1/query?query=up&time=now, or start=2024/01/02, or any non-numeric, non-RFC3339 value for a named time parameter.

Common situations: Passing human-friendly strings like 'now', '1h ago', or local-format dates (dd/mm/yyyy), forgetting URL-encoding so '+' in RFC3339 becomes a space, or passing millisecond epochs where seconds are expected.

Related errors


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

Appendix: source

Thrown at pkg/api/query/v1.go:1585

	}
	if end.Before(start) {
		return time.Time{}, time.Time{}, &api.ApiError{
			Typ: api.ErrorBadData,
			Err: errors.New("end timestamp must not be before start time"),
		}
	}

	return start, end, nil
}

func parseTimeParam(r *http.Request, paramName string, defaultValue time.Time) (time.Time, error) {
	val := r.FormValue(paramName)
	if val == "" {
		return defaultValue, nil
	}
	result, err := parseTime(val)
	if err != nil {
		return time.Time{}, errors.Wrapf(err, "Invalid time value for '%s'", paramName)
	}
	return result, nil
}

func parseTime(s string) (time.Time, error) {
	if t, err := strconv.ParseFloat(s, 64); err == nil {
		s, ns := math.Modf(t)
		ns = math.Round(ns*1000) / 1000
		return time.Unix(int64(s), int64(ns*float64(time.Second))), nil
	}
	if t, err := time.Parse(time.RFC3339Nano, s); err == nil {
		return t, nil
	}
	return time.Time{}, errors.Errorf("cannot parse %q to a valid timestamp", s)
}

func parseDuration(s string) (time.Duration, error) {
	if d, err := strconv.ParseFloat(s, 64); err == nil {

View on GitHub (pinned to 35b8b99117)