Skip to content

BindFlags does not work well with nested keys #2089

@jonsch318

Description

@jonsch318

Preflight Checklist

  • I have searched the issue tracker for an issue that matches the one I want to file, without success.
  • I am not looking for support or already pursued the available support channels without success.
  • I have checked the troubleshooting guide for my problem, without success.

Viper Version

1.21.0

Go Version

1.25.3

Config Source

Flags

Format

Other (specify below)

Repl.it link

https://replit.com/@JohnnyS3181/BindPFlag-with-nested-key?v=1

Code reproducing the issue

package main

import (
	"log/slog"

	"github.com/spf13/pflag"
	"github.com/spf13/viper"
)

type TestConfig struct {
	TestField  bool `mapstructure:"test_field"`
	TestField2 bool `mapstructure:"test_field2"`
}

func main() {
	// Define the flag using pflag (recommended with Viper)
	pflag.Bool("test-flag", false, "Test flag description")

	// Parse the flags
	pflag.Parse()

	conf := viper.New()

	// Bind pflag to Viper
	_ = conf.BindPFlag("test.test_field", pflag.Lookup("test-flag"))

	conf.SetDefault("test.test_field", false)
	conf.Set("test.test_field2", true)

	subConf := conf.Sub("test")
	if subConf == nil {
		slog.Error("No sub conf")
		return
	}

	slog.Info("ALL Keys", "keys", subConf.AllKeys())

	var config TestConfig
	if err := subConf.Unmarshal(&config); err != nil {
		slog.Error("Error during unmarshal", "err", err)
	}

	slog.Info("TestField (Direct)", "val", conf.GetBool("test.test_field"))
	slog.Info("TestField", "val", config.TestField)

	slog.Info("TestField2 via Set (Direct)", "val", conf.GetBool("test.test_field2"))
	slog.Info("TestField2 via Set", "val", config.TestField2)
}

Expected Behavior

I expect the unmarshalled test_field to be true aswell since when accessing it directly with the nested key it is correct.

I would expect something like

2025/11/01 14:44:47 INFO ALL Keys keys=[test_field, test_field2]
2025/11/01 14:44:47 INFO TestField (Direct) val=true
2025/11/01 14:44:47 INFO TestField val=true
2025/11/01 14:44:47 INFO TestField2 via Set (Direct) val=true
2025/11/01 14:44:47 INFO TestField2 via Set val=true

Actual Behavior

2025/11/01 14:44:47 INFO ALL Keys keys=[test_field2]
2025/11/01 14:44:47 INFO TestField (Direct) val=true
2025/11/01 14:44:47 INFO TestField val=false
2025/11/01 14:44:47 INFO TestField2 via Set (Direct) val=true
2025/11/01 14:44:47 INFO TestField2 via Set val=true

As seen here the test_field2 is correcty unmarshalled, but test_field is false even though test.test_field is true.

Steps To Reproduce

No response

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions