{"record":{"id":"8ce9563cf891447d","repo":"SigNoz/signoz","slug":"delete-ttl-should-be-greater-than-cold-storage-mov","errorCode":null,"errorMessage":"delete TTL should be greater than cold storage move TTL","messagePattern":"delete TTL should be greater than cold storage move TTL","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/query-service/app/parser.go","lineNumber":455,"sourceCode":"\t\treturn nil, fmt.Errorf(\"type param should be metrics|traces|logs, got %v\", typeTTL)\n\t}\n\n\t// Validate the TTL duration.\n\tdurationParsed, err := time.ParseDuration(delDuration)\n\tif err != nil || durationParsed.Seconds() <= 0 {\n\t\treturn nil, fmt.Errorf(\"not a valid TTL duration %v\", delDuration)\n\t}\n\n\tvar toColdParsed time.Duration\n\n\t// If some cold storage is provided, validate the cold storage move TTL.\n\tif len(coldStorage) > 0 {\n\t\ttoColdParsed, err = time.ParseDuration(toColdDuration)\n\t\tif err != nil || toColdParsed.Seconds() <= 0 {\n\t\t\treturn nil, fmt.Errorf(\"not a valid toCold TTL duration %v\", toColdDuration)\n\t\t}\n\t\tif toColdParsed.Seconds() != 0 && toColdParsed.Seconds() >= durationParsed.Seconds() {\n\t\t\treturn nil, fmt.Errorf(\"delete TTL should be greater than cold storage move TTL\")\n\t\t}\n\t}\n\n\treturn &retentiontypes.TTLParams{\n\t\tType:                  typeTTL,\n\t\tDelDuration:           int64(durationParsed.Seconds()),\n\t\tColdStorageVolume:     coldStorage,\n\t\tToColdStorageDuration: int64(toColdParsed.Seconds()),\n\t}, nil\n}\n\nfunc parseGetTTL(r *http.Request) (*retentiontypes.GetTTLParams, error) {\n\n\ttypeTTL := r.URL.Query().Get(\"type\")\n\n\tif len(typeTTL) == 0 {\n\t\treturn nil, fmt.Errorf(\"type param cannot be empty from the query\")\n\t} else {","sourceCodeStart":437,"sourceCodeEnd":473,"githubUrl":"https://github.com/SigNoz/signoz/blob/5069bf80b08f1f00d7e014eccc09902f9871004f/pkg/query-service/app/parser.go#L437-L473","documentation":"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.","triggerScenarios":"?duration=24h&toColdDuration=24h, or ?duration=24h&toColdDuration=48h — any case where the move-to-cold age meets or exceeds the deletion age.","commonSituations":"Misreading which param is which and swapping them; incrementing retention duration without adjusting toColdDuration; equal-value configs.","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"],"exampleFix":"// before\n?type=traces&duration=24h&coldStorage=s3&toColdDuration=48h\n// after\n?type=traces&duration=72h&coldStorage=s3&toColdDuration=24h","handlingStrategy":"validation","validationCode":"del, _ := time.ParseDuration(duration)\nmove, _ := time.ParseDuration(toColdDuration)\nif move >= del { return fmt.Errorf(\"toColdDuration must be < duration\") }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Derive toColdDuration from duration programmatically (e.g. duration/3) instead of hardcoding both","Alert on config review when either value changes"],"tags":["signoz","retention","cold-storage","ttl-constraint"],"backgroundTag":"invalid-duration-format","analyzedSha":"5069bf80b08f1f00d7e014eccc09902f9871004f","analyzedAt":"2026-08-28T06:22:12.824Z","schemaVersion":2},"datasetVersion":"2026-08-28T11:17:15.048Z"}