vitessio/vitess · error

failed to load flag for %s: %w

Error message

failed to load flag for %s: %w

What it means

BindFlags in go/viperutil/internal/value wraps any error returned by val.Flag(fs) into "failed to load flag for <key>" and panics. Because flag registration happens at startup wiring time, a failure here is considered unrecoverable programmer error: a Registerable value could not produce its pflag definition for the given flag set.

Source

Thrown at go/viperutil/internal/value/value.go:106

	for _, alias := range val.Aliases {
		v.RegisterAlias(alias, val.Key())
	}

	if len(val.EnvVars) > 0 {
		vars := append([]string{val.Key()}, val.EnvVars...)
		_ = v.BindEnv(vars...)
	}
}

// BindFlags creates bindings between each value's registry and the given flag
// set. This function will panic if any of the values defines a flag that does
// not exist in the flag set.
func BindFlags(fs *pflag.FlagSet, values ...Registerable) {
	for _, val := range values {
		flag, err := val.Flag(fs)
		switch {
		case err != nil:
			panic(fmt.Errorf("failed to load flag for %s: %w", val.Key(), err))
		case flag == nil:
			continue
		}

		_ = val.Registry().BindPFlag(val.Key(), flag)
		if flag.Name != val.Key() {
			val.Registry().RegisterAlias(flag.Name, val.Key())
		}
	}
}

// Static is a static value. Static values register to the Static registry, and
// do not respond to changes to config files. Their Get() method will return the
// same value for the lifetime of the process.
type Static[T any] struct {
	*Base[T]
}

View on GitHub (pinned to 01a25a7d17)

Solutions

  1. Read the wrapped inner error in the panic message — it names the underlying pflag failure; fix that first.
  2. Ensure each Registerable is bound exactly once per FlagSet; remove duplicate BindFlags calls for the same key.
  3. Verify no two registered values share the same Key(); rename one of the flags.
  4. Confirm you pass the same *pflag.FlagSet that the value was designed to register into.

Example fix

// before
fs := pflag.NewFlagSet("cmd", pflag.ContinueOnError)
viperutil.BindFlags(fs, val1)
viperutil.BindFlags(fs, val1) // panics: flag redefined
// after
viperutil.BindFlags(fs, val1)
Defensive patterns

Strategy: validation

Validate before calling

seen := map[string]bool{}
for _, v := range values {
	if seen[v.Key()] {
		panic(fmt.Sprintf("duplicate flag key %q passed to BindFlags", v.Key()))
	}
	seen[v.Key()] = true
}

Try / catch

// BindFlags panics; wrap at binary startup
func mustBindFlags(fs *pflag.FlagSet, vals ...viperutil.Registerable) {
	defer func() {
		if r := recover(); r != nil {
			log.Error("flag binding failed", slog.Any("panic", r))
			os.Exit(1)
		}
	}()
	viperutil.BindFlags(fs, vals...)
}

Prevention

When it happens

Trigger: Calling viperutil.BindFlags(fs, vals...) where a Registerable's Flag() method returns a non-nil error — e.g. duplicate flag name already registered on fs, invalid flag name characters, or a value type whose flag factory fails.

Common situations: Registering the same flag key twice in one FlagSet ("flag redefined"); building a custom binary that binds both plugin-defined and built-in flags that collide; passing flags registered on multiple different FlagSets to a single BindFlags call.

Related errors


AI-assisted analysis of vitessio/vitess@01a25a7d17 (2026-09-01). Data as JSON: /api/errors/03acc8a63808608e. Report an issue: GitHub.