thanos-io/thanos · error · api.ApiError
end timestamp must not be before start time
Error message
end timestamp must not be before start time
What it means
In queryRange, after parsing the `start` and `end` timestamps, the API rejects requests where `end` is earlier than `start`, returning HTTP 400 (api.ErrorBadData). This mirrors Prometheus' range-query semantics and prevents nonsensical or inverted time ranges.
Solutions
- Swap or recompute start/end so end >= start before sending the request.
- Normalize all timestamps to RFC3339/UTC in the client before building the URL.
- Add client-side validation: if end.Before(start), reject the query before the HTTP call.
Example fix
// before GET /api/v1/query_range?query=up&start=2026-01-02T00:00:00Z&end=2026-01-01T00:00:00Z&step=1m // after GET /api/v1/query_range?query=up&start=2026-01-01T00:00:00Z&end=2026-01-02T00:00:00Z&step=1m
Defensive patterns
Strategy: validation
Validate before calling
const start = Date.parse(startStr), end = Date.parse(endStr);
if (Number.isNaN(start) || Number.isNaN(end)) throw new Error('invalid timestamps');
if (end < start) throw new Error('end must not be before start'); Try / catch
const body = await res.json();
if (body.status === 'error' && /end timestamp must not be before start/.test(body.error)) {
// swap or recompute the range client-side before retrying
} Prevention
- Normalize all timestamps to UTC RFC3339 before building requests.
- Validate end >= start in every client that constructs range queries.
- Beware mixed timezones when users pick 'from'/'to' in dashboards.
When it happens
Trigger: Calling /api/v1/query_range with an `end` timestamp strictly before `start`, e.g. start=2026-01-02T00:00:00Z&end=2026-01-01T00:00:00Z, or where client clocks/timezone handling produce an inverted range.
Common situations: Client computing start/end with mixed timezones or RFC3339 vs Unix seconds mixups; dashboards where the 'to' field was set before the 'from' field; off-by-negative duration math when building relative ranges.
Understand the failure class
Background: "Invalid query parameter" / "Failed to parse value of ...": fixing bad query string parameters across APIs — this error's family across 36 libraries.
Related errors
- zero or negative query resolution step widths are not…
- could not unmarshal parameter
- engine type must be 'thanos'
- exceeded maximum resolution of 11,000 points per…
- invalid argument: --min-time
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/629eda3147617ffb.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/api/query/v1.go:778
if apiErr != nil {
return nil, nil, apiErr, func() {}
}
if engineParam != PromqlEngineThanos {
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: errors.New("engine type must be 'thanos'")}, func() {}
}
queryParam := qapi.parseQueryParam(r)
start, err := parseTime(r.FormValue("start"))
if err != nil {
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
}
end, err := parseTime(r.FormValue("end"))
if err != nil {
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
}
if end.Before(start) {
err := errors.New("end timestamp must not be before start time")
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
}
step, apiErr := qapi.parseStep(r, qapi.defaultRangeQueryStep, int64(end.Sub(start)/time.Second))
if apiErr != nil {
return nil, nil, apiErr, func() {}
}
if step <= 0 {
err := errors.New("zero or negative query resolution step widths are not accepted. Try a positive integer")
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}
}
// For safety, limit the number of returned points per timeseries.
// This is sufficient for 60s resolution for a week or 1h resolution for a year.
if end.Sub(start)/step > 11000 {
err := errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)")
return nil, nil, &api.ApiError{Typ: api.ErrorBadData, Err: err}, func() {}View on GitHub (pinned to 35b8b99117)