{"record":{"id":"0a3fd727ed99e790","repo":"jaegertracing/jaeger","slug":"ttl-must-be-a-whole-number-of-seconds","errorCode":null,"errorMessage":"ttl must be a whole number of seconds","messagePattern":"ttl must be a whole number of seconds","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/v2/clickhouse/config.go","lineNumber":86,"sourceCode":"\treturn Configuration{\n\t\tProtocol:                      defaultProtocol,\n\t\tDatabase:                      defaultDatabase,\n\t\tDefaultSearchDepth:            defaultSearchDepth,\n\t\tMaxSearchDepth:                defaultMaxSearchDepth,\n\t\tAttributeMetadataCacheTTL:     defaultAttributeMetadataCacheTTL,\n\t\tAttributeMetadataCacheMaxSize: defaultAttributeMetadataCacheMaxSize,\n\t}\n}\n\nfunc (cfg *Configuration) Validate() error {\n\tif _, err := govalidator.ValidateStruct(cfg); err != nil {\n\t\treturn err\n\t}\n\tif cfg.TTL < 0 {\n\t\treturn errors.New(\"ttl must be a non-negative duration\")\n\t}\n\tif cfg.TTL > 0 && cfg.TTL%time.Second != 0 {\n\t\treturn errors.New(\"ttl must be a whole number of seconds\")\n\t}\n\t// A search depth of zero would make every trace search return nothing, and a\n\t// negative one is meaningless, so reject both rather than querying with them.\n\tif cfg.DefaultSearchDepth <= 0 {\n\t\treturn errors.New(\"default_search_depth must be a positive number\")\n\t}\n\tif cfg.MaxSearchDepth <= 0 {\n\t\treturn errors.New(\"max_search_depth must be a positive number\")\n\t}\n\tif cfg.AttributeMetadataCacheTTL < 0 {\n\t\treturn errors.New(\"attribute_metadata_cache_ttl must be a non-negative duration\")\n\t}\n\tif cfg.AttributeMetadataCacheMaxSize < 0 {\n\t\treturn errors.New(\"attribute_metadata_cache_max_size must be a non-negative number\")\n\t}\n\treturn nil\n}\n","sourceCodeStart":68,"sourceCodeEnd":104,"githubUrl":"https://github.com/jaegertracing/jaeger/blob/806f4447841ecdb60519f408b004a599d515f437/internal/storage/v2/clickhouse/config.go#L68-L104","documentation":"ClickHouse Configuration.Validate returns this error when TTL is positive but not an exact multiple of one second. Sub-second TTL precision is not accepted, so fractional durations are rejected.","triggerScenarios":"Setting TTL to a value like 1500ms or 90.5s (i.e. TTL>0 with TTL%time.Second != 0) before creating the ClickHouse storage.","commonSituations":"Specifying TTL in milliseconds from a config that assumed a different unit; programmatic config built with time.Millisecond multiples; YAML parsing of fractional durations.","solutions":["Round TTL up to a whole number of seconds","Fix the unit conversion so the configured value is in seconds","Test the config through Validate in unit tests to catch fractional TTLs early"],"exampleFix":"// before\nttl: 1500 * time.Millisecond\n// after\nttl: 2 * time.Second","handlingStrategy":"validation","validationCode":"if cfg.TTL > 0 && cfg.TTL%time.Second != 0 {\n    return errors.New(\"ttl must be a whole number of seconds\")\n}","typeGuard":"func wholeSeconds(d time.Duration) bool { return d%time.Second == 0 }","tryCatchPattern":"if err := cfg.Validate(); err != nil {\n    if strings.Contains(err.Error(), \"ttl must be a whole number of seconds\") {\n        cfg.TTL = cfg.TTL.Round(time.Second)\n    }\n}","preventionTips":["Express TTLs in seconds or larger in config","Round durations when converting from ms-based systems","Unit-test Validate on boundary TTL values"],"tags":["clickhouse","configuration","ttl","jaeger"],"backgroundTag":"invalid-config-value","analyzedSha":"806f4447841ecdb60519f408b004a599d515f437","analyzedAt":"2026-09-01T02:39:22.140Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}