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

  1. Add a wtf.secretStore section to config.yml specifying the store program (e.g. type and command)
  2. Verify the config key spelling (wtf.secretStore) matches the schema
  3. Check newProgram()'s supported store types and configure one of them
  4. 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

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.