wtfutil/wtf · error
cannot store secrets: wtf.secretStore is not configured
Error message
cannot store secrets: wtf.secretStore is not configured
What it means
StoreSecret saves a credential through an external secret-store program (e.g. pass, security/kc) configured under wtf.secretStore. If newProgram() cannot build that program wrapper — meaning no secret store is configured in the global config — storage cannot proceed and this error is returned.
Source
Thrown at cfg/secrets.go:172
cred, err := client.Get(prog.runner, service)
if err != nil {
return nil, fmt.Errorf("get %v from %v: %w", service, prog.store, err)
}
return &Secret{
Service: cred.ServerURL,
Secret: cred.Secret,
Username: cred.Username,
Store: prog.store,
}, nil
}
func StoreSecret(globalConfig *config.Config, secret *Secret) error {
prog := newProgram(globalConfig)
if prog == nil {
return errors.New("cannot store secrets: wtf.secretStore is not configured")
}
cred := &credentials.Credentials{
ServerURL: secret.Service,
Username: secret.Username,
Secret: secret.Secret,
}
// docker-credential requires a username, but it isn't necessary for
// all services. Use a default if a username was not set.
if cred.Username == "" {
cred.Username = "default"
}
err := client.Store(prog.runner, cred)
if err != nil {
return fmt.Errorf("store %v: %w", prog.store, err)View on GitHub (pinned to bb838c1ccb)
Solutions
- Add a wtf.secretStore section to config.yml specifying the store program (e.g. type and command)
- Verify the config key spelling (wtf.secretStore) matches the schema
- Check newProgram()'s supported store types and configure one of them
- Guard the calling code: only call StoreSecret when the secret store is configured
Example fix
// before
wtf:
# no secretStore configured
// after
wtf:
secretStore:
type: keychain
prefix: wtfutil Defensive patterns
Strategy: validation
Validate before calling
if globalConfig == nil || globalConfig.Uint("wtf.secretStore", nil) == nil /* section absent */ {
return errors.New("configure wtf.secretStore before storing secrets")
} Type guard
func secretStoreConfigured(c *config.Config) bool {
return c != nil && c.Section("wtf").HasKey("secretStore")
} Try / catch
if err := cfg.StoreSecret(conf, secret); err != nil {
if strings.Contains(err.Error(), "not configured") {
return fmt.Errorf("add a wtf.secretStore section to config.yml: %w", err)
}
return err
} Prevention
- Add the secretStore section during initial setup, not later
- Validate config.yml against the wtf schema on startup
- Keep the exact key casing: wtf.secretStore
- Document the required store type for your team's modules
When it happens
Trigger: Calling StoreSecret (directly or via RenderIf secret rendering) with a config that lacks a wtf.secretStore section; the config file loads but the secretStore block was deleted/renamed; using defaults without configuring a store while a module tries to persist a secret.
Common situations: Fresh wtfutil installs where users add a module requiring secrets but never configure the secretStore; config migrations dropping the secretStore key; typo'd key name so the section is nil at runtime.
Related errors
AI-assisted analysis of wtfutil/wtf@bb838c1ccb (2026-09-03).
Data as JSON: /api/errors/9588a4a45adfd474.
Report an issue: GitHub.