thanos-io/thanos · error

invalid duration

Error message

invalid duration

What it means

Thrown by the Unmarshal method of queryrange.Duration (a gogo-protobuf generated wrapper around time.Duration) when the incoming value is neither a number/protobuf duration nor a valid string form. The decoder falls into the `default` branch of a type switch, meaning the JSON/YAML field holding a duration had an unrecognized type (e.g. an object, bool, or malformed scalar).

Solutions

  1. Send the duration as a numeric nanosecond value or a quoted string like "1m"/"500ms"
  2. Check the JSON payload's Content-Type and body for type errors in duration fields
  3. Update the client SDK/serializer to emit protobuf-compatible duration encodings

Example fix

// before
{"query":"up","step":five-minutes}
// after
{"query":"up","step":"5m"}
Defensive patterns

Strategy: validation

Validate before calling

func validDuration(v interface{}) bool {
	switch t := v.(type) {
	case float64:
		return t >= 0
	case string:
		_, err := time.ParseDuration(t)
		return err == nil
	}
	return false
}

Type guard

if d, ok := raw.(string); !ok { return errors.New("duration must be string") }

Try / catch

if err := json.Unmarshal(body, &req); err != nil {
	if strings.Contains(err.Error(), "invalid duration") {
		return http.StatusBadRequest("duration fields must be numeric ns or quoted like \"5m\"")
	}
}

Prevention

When it happens

Trigger: Deserializing a query-range request or config where a duration field (step, interval, timeout) is supplied as a non-numeric, non-string JSON value — e.g. `"step": true`, `"step": {}`, or a string protobuf cannot parse as a duration.

Common situations: Hand-written JSON bodies posted to the query-frontend API; templated configs where a duration placeholder renders as a wrong type; clients sending step as a bare word like `step` instead of `"1m"`.

Understand the failure class

Background: "invalid duration" / "failed to parse duration": why your timeout, interval, or TTL string is rejected and which formats each library accepts — this error's family across 32 libraries.

Related errors


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

Appendix: source

Thrown at internal/cortex/querier/queryrange/query_range.go:955

func (d *Duration) UnmarshalJSON(b []byte) error {
	var v any
	if err := json.Unmarshal(b, &v); err != nil {
		return err
	}
	switch value := v.(type) {
	case float64:
		*d = Duration(time.Duration(value))
		return nil
	case string:
		tmp, err := time.ParseDuration(value)
		if err != nil {
			return err
		}
		*d = Duration(tmp)
		return nil
	default:
		return errors.New("invalid duration")
	}
}

func (d *Duration) Size() int {
	return github_com_gogo_protobuf_types.SizeOfStdDuration(time.Duration(*d))
}

func (d *Duration) Unmarshal(b []byte) error {
	var td time.Duration
	if err := github_com_gogo_protobuf_types.StdDurationUnmarshal(&td, b); err != nil {
		return err
	}
	*d = Duration(td)
	return nil
}

func (d *Duration) MarshalTo(b []byte) (int, error) {
	return github_com_gogo_protobuf_types.StdDurationMarshalTo(time.Duration(*d), b)

View on GitHub (pinned to 35b8b99117)