thanos-io/thanos · error
load tenant limits config
Error message
load tenant limits config
What it means
NewLimiter wraps any failure from limiter.loadConfig() — reading and parsing the tenant limits file or content — with the message 'load tenant limits config'. The root cause is in the wrapped error (file missing, unreadable, or invalid YAML/JSON), and the limiter fails to initialize.
Solutions
- Check the wrapped (%v) cause: if file-not-found, fix the --receive-limit-config path or mount the file
- Verify the file is readable by the process user (permissions, SELinux/container securityContext)
- Validate the config content parses (run ParseLimitConfigContent / a YAML linter) before startup
- If config is optional, pass nil/empty so NewLimiter skips loading (configFile == nil path)
- Set up StartConfigReloader only after NewLimiter succeeds; fix config before restart
Example fix
// before
limiter, err := NewLimiter(hashrings, backend, limitsConfigPath, ...)
// if limitsConfigPath is wrong: load tenant limits config: open /etc/thanos/limits.yml: no such file
// after
if _, err := os.Stat(limitsConfigPath); err != nil {
log.Fatalf("limit config missing: %v", err)
}
limiter, err := NewLimiter(hashrings, backend, limitsConfigPath, ...) Defensive patterns
Strategy: try-catch
Validate before calling
if limitCfgPath != "" {
if _, err := os.Stat(limitCfgPath); err != nil {
return nil, fmt.Errorf("limits config path invalid: %w", err)
}
} Try / catch
limiter, err := receive.NewLimiter(hashrings, bkt, cfgPath, limits, log)
if err != nil {
return fmt.Errorf("starting receiver limiter: %w", err) // inspect wrapped cause
} Prevention
- Mount the limits config file and verify existence in the entrypoint script
- Fail readiness checks until the limiter initializes
- Pass nil when no limit config is intended
When it happens
Trigger: Calling NewLimiter with a configFile path or content whose loadConfig() fails: the file does not exist, lacks read permission, or contains invalid limit config.
Common situations: Wrong path passed to the receiver's limit-config flag; file not mounted into the pod/container; config file not yet generated at startup; permissions changed after deployment.
Understand the failure class
Background: "Config file not found": what it means and how to fix it in docker-sync, Maven, Vagrant, Turborepo and other tools — this error's family across 60 libraries.
Related errors
- create syncer
- unable to load config file
- unable to create config reloader
- unable to load config initially
- tracing failed
AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07).
Data as JSON: /api/errors/8d5d5fb21a20855c.
Report an issue: GitHub.
Appendix: source
Thrown at pkg/receive/limiter.go:103
},
)
limiter.configReloadFailedCounter = promauto.With(limiter.registerer).NewCounter(
prometheus.CounterOpts{
Namespace: "thanos",
Subsystem: "receive",
Name: "limits_config_reload_err_total",
Help: "How many times the limit configuration failed to reload.",
},
)
}
if configFile == nil {
return limiter, nil
}
limiter.configPathOrContent = configFile
if err := limiter.loadConfig(); err != nil {
return nil, errors.Wrap(err, "load tenant limits config")
}
return limiter, nil
}
// StartConfigReloader starts the automatic configuration reloader based off of
// the file indicated by pathOrContent. It starts a Go routine in the given
// *run.Group.
func (l *Limiter) StartConfigReloader(ctx context.Context) error {
if !l.CanReload() {
return nil
}
return extkingpin.PathContentReloader(ctx, l.configPathOrContent, l.logger, func() {
level.Info(l.logger).Log("msg", "reloading limit config")
if err := l.loadConfig(); err != nil {
if failedReload := l.configReloadCounter; failedReload != nil {
failedReload.Inc()View on GitHub (pinned to 35b8b99117)