jaegertracing/jaeger · error

ttl must be a whole number of seconds

Error message

ttl must be a whole number of seconds

What it means

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.

Source

Thrown at internal/storage/v2/clickhouse/config.go:86

	return Configuration{
		Protocol:                      defaultProtocol,
		Database:                      defaultDatabase,
		DefaultSearchDepth:            defaultSearchDepth,
		MaxSearchDepth:                defaultMaxSearchDepth,
		AttributeMetadataCacheTTL:     defaultAttributeMetadataCacheTTL,
		AttributeMetadataCacheMaxSize: defaultAttributeMetadataCacheMaxSize,
	}
}

func (cfg *Configuration) Validate() error {
	if _, err := govalidator.ValidateStruct(cfg); err != nil {
		return err
	}
	if cfg.TTL < 0 {
		return errors.New("ttl must be a non-negative duration")
	}
	if cfg.TTL > 0 && cfg.TTL%time.Second != 0 {
		return errors.New("ttl must be a whole number of seconds")
	}
	// A search depth of zero would make every trace search return nothing, and a
	// negative one is meaningless, so reject both rather than querying with them.
	if cfg.DefaultSearchDepth <= 0 {
		return errors.New("default_search_depth must be a positive number")
	}
	if cfg.MaxSearchDepth <= 0 {
		return errors.New("max_search_depth must be a positive number")
	}
	if cfg.AttributeMetadataCacheTTL < 0 {
		return errors.New("attribute_metadata_cache_ttl must be a non-negative duration")
	}
	if cfg.AttributeMetadataCacheMaxSize < 0 {
		return errors.New("attribute_metadata_cache_max_size must be a non-negative number")
	}
	return nil
}

View on GitHub (pinned to 806f444784)

Solutions

  1. Round TTL up to a whole number of seconds
  2. Fix the unit conversion so the configured value is in seconds
  3. Test the config through Validate in unit tests to catch fractional TTLs early

Example fix

// before
ttl: 1500 * time.Millisecond
// after
ttl: 2 * time.Second
Defensive patterns

Strategy: validation

Validate before calling

if cfg.TTL > 0 && cfg.TTL%time.Second != 0 {
    return errors.New("ttl must be a whole number of seconds")
}

Type guard

func wholeSeconds(d time.Duration) bool { return d%time.Second == 0 }

Try / catch

if err := cfg.Validate(); err != nil {
    if strings.Contains(err.Error(), "ttl must be a whole number of seconds") {
        cfg.TTL = cfg.TTL.Round(time.Second)
    }
}

Prevention

When it happens

Trigger: Setting TTL to a value like 1500ms or 90.5s (i.e. TTL>0 with TTL%time.Second != 0) before creating the ClickHouse storage.

Common situations: Specifying TTL in milliseconds from a config that assumed a different unit; programmatic config built with time.Millisecond multiples; YAML parsing of fractional durations.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jaegertracing/jaeger@806f444784 (2026-09-01). Data as JSON: /api/errors/0a3fd727ed99e790. Report an issue: GitHub.