gastownhall/beads · error

refusing to write secret key %q to git-tracked config file %

Error message

refusing to write secret key %q to git-tracked config file %s

This would expose your secret in git history. Instead:
  export %s="your-key-here"    # add to ~/.secrets or ~/.zshrc

Or move config.yaml out of git tracking:
  git rm --cached %s
  echo "config.yaml" >> %s/.gitignore

To override this check (e.g., for testing):
  bd config set --force-git-tracked %s "value"

What it means

checkSecretGitTracked refuses to persist a secret-type config key (e.g. secret.* keys) into a config.yaml that is tracked by git, because the value would leak into git history. It throws a detailed error with remediation (env var, git rm --cached, .gitignore) and an explicit --force-git-tracked override. This is a deliberate safety guard, not a bug.

Source

Thrown at internal/config/yaml_config.go:233

	configPath, err := findProjectConfigYaml()
	if err != nil {
		return nil // can't resolve path; let the write fail with its own error
	}
	return checkSecretGitTracked(configPath, key)
}

func checkSecretGitTracked(configPath, key string) error {
	if !IsYamlOnlyKey(key) {
		return nil
	}
	if !IsSecretKey(key) {
		return nil
	}
	if !isGitTracked(configPath) {
		return nil
	}
	envVar := secretKeyEnvVarHint(key)
	return fmt.Errorf(
		"refusing to write secret key %q to git-tracked config file %s\n\n"+
			"This would expose your secret in git history. Instead:\n"+
			"  export %s=\"your-key-here\"    # add to ~/.secrets or ~/.zshrc\n\n"+
			"Or move config.yaml out of git tracking:\n"+
			"  git rm --cached %s\n"+
			"  echo \"config.yaml\" >> %s/.gitignore\n\n"+
			"To override this check (e.g., for testing):\n"+
			"  bd config set --force-git-tracked %s \"value\"",
		key, configPath,
		envVar,
		configPath,
		filepath.Dir(configPath),
		key,
	)
}

// keyAliases maps alternative key names to their canonical yaml form.
// This ensures consistency when users use different formats (dot vs hyphen).

View on GitHub (pinned to 71377f2769)

Solutions

  1. Store the key in the environment instead: export <ENV_VAR>="your-key-here" (the error names the exact env var via secretKeyEnvVarHint).
  2. Untrack the file: git rm --cached <config.yaml> and add it to .gitignore, then rewrite.
  3. Re-run with bd config set --force-git-tracked <key> "value" only if you consciously accept the exposure (e.g. test fixtures).
  4. Rotate the key if it was ever committed to history (git filter-repo / BFG) since the guard may have been bypassed previously.

Example fix

// before
bd config set secret.anthropic_key sk-ant-...   # config.yaml is git-tracked -> refused
// after
export ANTHROPIC_KEY=sk-ant-...
git rm --cached .beads/config.yaml
echo ".beads/config.yaml" >> .gitignore
Defensive patterns

Strategy: validation

Validate before calling

func isGitTracked(path string) bool {
    return exec.Command("git", "ls-files", "--error-unmatch", path).Run() == nil
}
if isGitTracked(configPath) && strings.HasPrefix(key, "secret.") {
    os.Setenv(secretEnvVar(key), value) // use env instead
    return nil
}

Type guard

func safeForConfigWrite(key, configPath string) bool {
    return !strings.HasPrefix(key, "secret.") || !isGitTracked(configPath)
}

Try / catch

err := cfg.CheckSecretKeyGitSafety(key, configPath)
if err != nil && strings.Contains(err.Error(), "refusing to write secret key") {
    // fall back to env var per the error's remediation
    os.Setenv(envHint, value)
    return nil
}

Prevention

When it happens

Trigger: Calling CheckSecretKeyGitSafety / the config writer (bd config set) for a secret key while configPath is inside a git repo and `git ls-files` shows config.yaml is tracked.

Common situations: Committing config.yaml with secret.* keys in a project repo; initializing bd inside a repo where config.yaml was previously committed; team-shared repos where users keep keys in config instead of the environment.

Related errors


AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30). Data as JSON: /api/errors/3bf57f291de727b1. Report an issue: GitHub.