ipfs/kubo · error

Provide.DHT.Interval (%v) must be less than or equal to DHT

Error message

Provide.DHT.Interval (%v) must be less than or equal to DHT provider record validity (%v)

What it means

DHT provider records expire after amino.DefaultProvideValidity; if the reprovide interval Provide.DHT.Interval exceeds that validity, records would lapse and content would disappear from the DHT between reprovides. ValidateProvideConfig rejects any explicitly set interval greater than the record validity to keep announcements continuous.

Source

Thrown at config/provide.go:215

	// Validate Provide.Strategy
	strategy := cfg.Strategy.WithDefault(DefaultProvideStrategy)
	if _, err := ParseProvideStrategy(strategy); err != nil {
		return fmt.Errorf("Provide.Strategy: %w", err)
	}

	// Validate Provide.BloomFPRate
	if !cfg.BloomFPRate.IsDefault() {
		rate := cfg.BloomFPRate.WithDefault(DefaultProvideBloomFPRate)
		if rate < MinProvideBloomFPRate {
			return fmt.Errorf("Provide.BloomFPRate must be >= %d (1 in 1M), got %d", MinProvideBloomFPRate, rate)
		}
	}

	// Validate Provide.DHT.Interval
	if !cfg.DHT.Interval.IsDefault() {
		interval := cfg.DHT.Interval.WithDefault(DefaultProvideDHTInterval)
		if interval > amino.DefaultProvideValidity {
			return fmt.Errorf("Provide.DHT.Interval (%v) must be less than or equal to DHT provider record validity (%v)", interval, amino.DefaultProvideValidity)
		}
		if interval < 0 {
			return fmt.Errorf("Provide.DHT.Interval must be non-negative, got %v", interval)
		}
		// Provide.DHT.Interval=0 used to disable the entire provide system as a
		// side effect. It now disables only the periodic reprovide schedule:
		// new CIDs still announce via fast-provide-root and 'ipfs provide once'.
		// Operators upgrading from earlier kubo versions must opt in to one of
		// the two semantics by setting Provide.Enabled explicitly:
		//   - Provide.Enabled=false fully disables providing (the old behaviour).
		//   - Provide.Enabled=true keeps ad-hoc providing while disabling the
		//     periodic reprovide schedule.
		if interval == 0 && cfg.Enabled == Default {
			return fmt.Errorf("Provide.DHT.Interval=0 no longer disables the provide system on its own; set Provide.Enabled explicitly: " +
				"Provide.Enabled=false to fully disable providing, or Provide.Enabled=true to keep ad-hoc 'ipfs provide once' " +
				"and fast-provide-root working while skipping the periodic reprovide schedule")
		}
	}

View on GitHub (pinned to 329838acdf)

Solutions

  1. Lower Provide.DHT.Interval to a duration <= amino.DefaultProvideValidity (the default is 22h, leaving headroom under the 24h validity)
  2. Remove Provide.DHT.Interval to restore the 22h default
  3. If reducing DHT traffic is the goal, use Provide.Strategy (e.g. "pinned+unique") instead of stretching the interval

Example fix

// before
"Interval": "48h"
// after
"Interval": "22h"
Defensive patterns

Strategy: validation

Validate before calling

if iv := cfg.Provide.DHT.Interval.WithDefault(config.DefaultProvideDHTInterval); !cfg.Provide.DHT.Interval.IsDefault() && iv > amino.DefaultProvideValidity {
	return fmt.Errorf("interval %v exceeds provider record validity %v", iv, amino.DefaultProvideValidity)
}

Try / catch

if err := config.ValidateProvideConfig(cfg.Provide); err != nil {
	if strings.Contains(err.Error(), "less than or equal to DHT provider record validity") {
		cfg.Provide.DHT.Interval = nil // use 22h default
	}
	return err
}

Prevention

When it happens

Trigger: Node startup with Provide.DHT.Interval explicitly set to a duration longer than amino.DefaultProvideValidity (24h in go-libp2p-kad-dht), e.g. "48h", via config file or `ipfs config --json Provide.DHT.Interval '"48h"'`.

Common situations: Operator trying to reduce DHT traffic stretches the reprovide interval past the record lifetime; copied interval from another implementation or docs assuming no upper bound.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/09f5646551c5a727. Report an issue: GitHub.