thanos-io/thanos · error

level field is empty

Error message

level field is empty

What it means

checkOptionsConfigEmpty validates gRPC logging interceptor options: a config is fully empty only if no level is set AND neither LogStart nor LogEnd is requested. If a log decision (LogStart or LogEnd) is requested but the level field is empty, the configuration is contradictory and this error is raised.

Solutions

  1. Set the 'level' field in the logging config (INFO/DEBUG/ERROR/WARNING).
  2. If no logging is wanted, remove the logStart/logEnd decisions instead of leaving level empty.
  3. Validate the YAML block before applying it with NewGRPCOption.

Example fix

// before
grpc:
  - logStart: true
// after
grpc:
  - level: INFO
    logStart: true
Defensive patterns

Strategy: validation

Validate before calling

if (decision.LogStart || decision.LogEnd) && level == "" {
  return fmt.Errorf("level must be set when logStart/logEnd is enabled")
}

Try / catch

opt, err := logging.NewGRPCOption(cfg)
if err != nil { return fmt.Errorf("invalid gRPC logging config: %w", err) }

Prevention

When it happens

Trigger: Creating gRPC logging options from YAML where a decision like logStart: true or logEnd: true is set but the 'level' field is omitted or empty string.

Common situations: Partially filled logging config blocks in the gRPC server options YAML; users setting log decisions under a different config key than 'level'.

Understand the failure class

Background: "is required", "must be set", "missing required field": configuration validation errors across open-source libraries — this error's family across 36 libraries.

Related errors


AI-assisted analysis of thanos-io/thanos@35b8b99117 (2026-09-07). Data as JSON: /api/errors/f581d7a5ae579e70. Report an issue: GitHub.

Appendix: source

Thrown at pkg/logging/grpc.go:33

// NewRequestConfig parses the string into a req logging config structure.
// Raise an error if unmarshalling is not possible, or values are not valid.
func NewRequestConfig(configYAML []byte) (*RequestConfig, error) {
	reqLogConfig := &RequestConfig{}
	if err := yaml.UnmarshalStrict(configYAML, reqLogConfig); err != nil {
		return nil, err
	}
	return reqLogConfig, nil
}

// checkOptionsConfigEmpty checks if the OptionsConfig struct is empty and valid.
// If invalid combination is present, return an error.
func checkOptionsConfigEmpty(optcfg OptionsConfig) (bool, error) {
	if optcfg.Level == "" && !optcfg.Decision.LogEnd && !optcfg.Decision.LogStart {
		return true, nil
	}
	if optcfg.Level == "" && (optcfg.Decision.LogStart || optcfg.Decision.LogEnd) {
		return false, fmt.Errorf("level field is empty")
	}
	return false, nil
}

// fillGlobalOptionConfig configures all the method to have global config for logging.
func fillGlobalOptionConfig(reqLogConfig *RequestConfig, isgRPC bool) (string, bool, bool, error) {
	globalLevel := "ERROR"
	globalStart, globalEnd := false, false

	globalOptionConfig := reqLogConfig.Options
	isEmpty, err := checkOptionsConfigEmpty(globalOptionConfig)

	// If the decision for logging is enabled with empty level field.
	if err != nil {
		return "", false, false, err
	}
	if !isEmpty {
		globalLevel = globalOptionConfig.Level

View on GitHub (pinned to 35b8b99117)