gastownhall/beads · error
key cannot start with 'kv.' (would create nested prefix)
Error message
key cannot start with 'kv.' (would create nested prefix)
What it means
Keys are stored under the `kv.` prefix internally; a user key beginning with `kv.` would create a nested `kv.kv.*` prefix, corrupting the namespace layout. validateKVKey rejects such keys to keep the key space flat.
Source
Thrown at cmd/bd/kv.go:29
"github.com/steveyegge/beads/internal/metrics"
"github.com/steveyegge/beads/internal/storage/kvkeys"
)
// kvPrefix is prepended to all user keys to separate them from internal config
const kvPrefix = kvkeys.Prefix
// validateKVKey checks if a key is valid for the KV store.
// Returns an error if the key is invalid.
func validateKVKey(key string) error {
if key == "" {
return fmt.Errorf("key cannot be empty")
}
if strings.TrimSpace(key) == "" {
return fmt.Errorf("key cannot be only whitespace")
}
// Prevent keys that would create nested kv.kv.* prefixes
if strings.HasPrefix(key, kvPrefix) {
return fmt.Errorf("key cannot start with 'kv.' (would create nested prefix)")
}
// Reserve the persistent-memory namespace: a generic memory.* key would
// store to kv.memory.*, indistinguishable from a `bd remember` memory, and
// the merge resolver auto-resolves kv.memory.* conflicts with --theirs
// (GH#2474). Without this guard a user's deliberate kv value could be
// silently overridden by a remote on pull. Keep the namespace owned by
// bd remember / bd forget.
if strings.HasPrefix(key, kvkeys.MemoryPrefix) {
return fmt.Errorf("key cannot start with %q (reserved for persistent memories; use 'bd remember' / 'bd forget')", kvkeys.MemoryPrefix)
}
// Prevent keys that look like internal config
if strings.HasPrefix(key, "sync.") || strings.HasPrefix(key, "conflict.") ||
strings.HasPrefix(key, "federation.") || strings.HasPrefix(key, "jira.") ||
strings.HasPrefix(key, "linear.") || strings.HasPrefix(key, "export.") ||
strings.HasPrefix(key, "import.") {
return fmt.Errorf("key cannot start with reserved prefix %q", strings.Split(key, ".")[0]+".")
}
return nilView on GitHub (pinned to 71377f2769)
Solutions
- Drop the `kv.` prefix: use bd kv set foo value instead of kv.foo.
- If migrating, strip the prefix programmatically before setting keys.
- Read `bd kv --help` for key naming rules.
Example fix
// before bd kv set kv.mykey "v" // rejected // after bd kv set mykey "v"
Defensive patterns
Strategy: validation
Validate before calling
case "$KEY" in kv.*) echo "strip the kv. prefix" >&2; exit 1;; esac bd kv set "$KEY" "$VALUE"
Prevention
- Never include the store prefix in user-supplied keys.
- Document key naming conventions for team scripts.
- Validate keys in a shared helper before any bd kv set call.
When it happens
Trigger: Running `bd kv set kv.foo value` or any key with the literal `kv.` prefix.
Common situations: Users assuming keys must repeat the store's prefix (writing `kv.mykey` because values live in kv.* internally); migration scripts from another tool that prefixed keys with `kv.`.
Related errors
- key cannot be empty
- key cannot be only whitespace
- key cannot start with %q (reserved for persistent memories;
- key cannot start with reserved prefix %q
- got %d close reasons for %d issue IDs; provide exactly one s
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/ae40aeaf8c313cae.
Report an issue: GitHub.