VictoriaMetrics/VictoriaMetrics · error

cannot set both token and bearer_token configs

Error message

cannot set both token and bearer_token configs

What it means

The consul_sd_config in VictoriaMetrics promscrape supports two mutually exclusive ways to authenticate requests to the Consul API: the `token` field (or CONSUL_HTTP_TOKEN / CONSUL_HTTP_TOKEN_FILE env vars) and the promauth `bearer_token` field inside `http_client_config`. When a token resolves to a non-empty value and `bearer_token` is also set, newAPIConfig refuses to build the API config because only one Authorization mechanism can be used, and returns this error at consul/api.go:48.

Source

Thrown at lib/promscrape/discovery/consul/api.go:48

var configMap = discoveryutil.NewConfigMap()

func getAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
	v, err := configMap.Get(sdc, func() (any, error) { return newAPIConfig(sdc, baseDir) })
	if err != nil {
		return nil, err
	}
	return v.(*apiConfig), nil
}

func newAPIConfig(sdc *SDConfig, baseDir string) (*apiConfig, error) {
	hcc := sdc.HTTPClientConfig
	token, err := GetToken(sdc.Token)
	if err != nil {
		return nil, err
	}
	if token != "" {
		if hcc.BearerToken != nil {
			return nil, fmt.Errorf("cannot set both token and bearer_token configs")
		}
		hcc.BearerToken = promauth.NewSecret(token)
	}
	if len(sdc.Username) > 0 {
		if hcc.BasicAuth != nil {
			return nil, fmt.Errorf("cannot set both username and basic_auth configs")
		}
		hcc.BasicAuth = &promauth.BasicAuthConfig{
			Username: sdc.Username,
			Password: sdc.Password,
		}
	}
	ac, err := hcc.NewConfig(baseDir)
	if err != nil {
		return nil, fmt.Errorf("cannot parse auth config: %w", err)
	}
	apiServer := sdc.Server
	if apiServer == "" {

View on GitHub (pinned to 5079fb58f1)

Solutions

  1. Remove the bearer_token field from the consul_sd_config's http_client_config and keep only `token`.
  2. Unset the CONSUL_HTTP_TOKEN / CONSUL_HTTP_TOKEN_FILE environment variables on theVictoriaMetrics process if bearer_token should take precedence instead.
  3. Keep only one mechanism: delete `token` from the config if bearer_token is the intended auth method.

Example fix

// before
consul_sd_config:
  server: "consul:8500"
  token: "my-consul-token"
  http_client_config:
    bearer_token: "other-token"
// after
consul_sd_config:
  server: "consul:8500"
  token: "my-consul-token"
Defensive patterns

Strategy: validation

Validate before calling

func validateConsulTokenConflict(sdc *consul.SDConfig) error {
    token, err := consul.GetToken(sdc.Token)
    if err != nil {
        return err
    }
    if token != "" && sdc.HTTPClientConfig.BearerToken != nil {
        return fmt.Errorf("consul_sd_config: set only one of `token` and `bearer_token`")
    }
    return nil
}

Type guard

func hasBothTokenAndBearer(sdc *consul.SDConfig) bool {
    token, _ := consul.GetToken(sdc.Token)
    return token != "" && sdc.HTTPClientConfig.BearerToken != nil
}

Try / catch

if _, err := consul.GetAPIConfig(sdc, baseDir); err != nil {
    if strings.Contains(err.Error(), "cannot set both token and bearer_token") {
        // fix config: drop one auth mechanism, then retry
        return fmt.Errorf("invalid consul_sd_config: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Calling getAPIConfig/newAPIConfig with an SDConfig where sdc.Token resolves to a non-empty string (via token field, CONSUL_HTTP_TOKEN, or CONSUL_HTTP_TOKEN_FILE) AND sdc.HTTPClientConfig.BearerToken is non-nil (bearer_token set in the scrape config).

Common situations: Operators migrating from token-only configs to a shared http_client_config block and leaving both keys present; CONSUL_HTTP_TOKEN or CONSUL_HTTP_TOKEN_FILE exported in the environment while the YAML also sets bearer_token; copy-pasting prometheus scrape configs where bearer_token was used and adding a consul token later.

Related errors


AI-assisted analysis of VictoriaMetrics/VictoriaMetrics@5079fb58f1 (2026-09-03). Data as JSON: /api/errors/68e733582ea184ef. Report an issue: GitHub.