SigNoz/signoz · error
delete TTL should be greater than cold storage move TTL
Error message
delete TTL should be greater than cold storage move TTL
What it means
Data must live on hot storage longer than the cold-storage move threshold, so toColdDuration must be strictly less than the overall delete TTL duration. If toColdParsed >= durationParsed the request is rejected.
Solutions
- Make duration strictly greater than toColdDuration (e.g. duration=72h, toColdDuration=24h)
- If you intended no cold tier, remove the coldStorage and toColdDuration params entirely
- Re-read the semantics: duration = total retention before delete; toColdDuration = age at which data moves to cold
Example fix
// before ?type=traces&duration=24h&coldStorage=s3&toColdDuration=48h // after ?type=traces&duration=72h&coldStorage=s3&toColdDuration=24h
Defensive patterns
Strategy: validation
Validate before calling
del, _ := time.ParseDuration(duration)
move, _ := time.ParseDuration(toColdDuration)
if move >= del { return fmt.Errorf("toColdDuration must be < duration") } Prevention
- Derive toColdDuration from duration programmatically (e.g. duration/3) instead of hardcoding both
- Alert on config review when either value changes
When it happens
Trigger: ?duration=24h&toColdDuration=24h, or ?duration=24h&toColdDuration=48h — any case where the move-to-cold age meets or exceeds the deletion age.
Common situations: Misreading which param is which and swapping them; incrementing retention duration without adjusting toColdDuration; equal-value configs.
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
- not a valid toCold TTL duration
- error while getting ttl. Err=
- not a valid TTL duration
- SetTTLV2 only supported
- type and duration param cannot be empty from the query
AI-assisted analysis of SigNoz/signoz@5069bf80b0 (2026-08-28).
Data as JSON: /api/errors/8ce9563cf891447d.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/query-service/app/parser.go:455
return nil, fmt.Errorf("type param should be metrics|traces|logs, got %v", typeTTL)
}
// Validate the TTL duration.
durationParsed, err := time.ParseDuration(delDuration)
if err != nil || durationParsed.Seconds() <= 0 {
return nil, fmt.Errorf("not a valid TTL duration %v", delDuration)
}
var toColdParsed time.Duration
// If some cold storage is provided, validate the cold storage move TTL.
if len(coldStorage) > 0 {
toColdParsed, err = time.ParseDuration(toColdDuration)
if err != nil || toColdParsed.Seconds() <= 0 {
return nil, fmt.Errorf("not a valid toCold TTL duration %v", toColdDuration)
}
if toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() {
return nil, fmt.Errorf("delete TTL should be greater than cold storage move TTL")
}
}
return &retentiontypes.TTLParams{
Type: typeTTL,
DelDuration: int64(durationParsed.Seconds()),
ColdStorageVolume: coldStorage,
ToColdStorageDuration: int64(toColdParsed.Seconds()),
}, nil
}
func parseGetTTL(r *http.Request) (*retentiontypes.GetTTLParams, error) {
typeTTL := r.URL.Query().Get("type")
if len(typeTTL) == 0 {
return nil, fmt.Errorf("type param cannot be empty from the query")
} else {View on GitHub (pinned to 5069bf80b0)