{"record":{"id":"abe8c52560f72068","repo":"gastownhall/beads","slug":"config-key-must-not-be-empty","errorCode":null,"errorMessage":"config key must not be empty","messagePattern":"config key must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/workapi/workspaceconfig.go","lineNumber":33,"sourceCode":"//\n// It lives here for the reason those do: three implementations of\n// issueops.WorkspaceConfig have to agree about it, and it is checkable without\n// a database. The conformance contract is left to pin what only a real backend\n// can show — that the row and its projection land together.\n//\n// It is deliberately NOT the whole of `bd config set`. Which SOURCE a key\n// belongs to (config.yaml, git config, this plane) is resolved by the front\n// door; see issueops/workspaceconfig.go.\n\n// ValidateSettingKey checks a key a caller wants to READ or REMOVE, and\n// returns it unchanged.\n//\n// The only rule at this end is that a key has to name something: an empty key\n// answered with the empty value an unset key returns would report \"not set\"\n// for a question nobody asked.\nfunc ValidateSettingKey(key string) (string, error) {\n\tif strings.TrimSpace(key) == \"\" {\n\t\treturn \"\", fmt.Errorf(\"%w: config key must not be empty\", issueops.ErrValidation)\n\t}\n\treturn key, nil\n}\n\n// ValidateSettingWrite checks a key and value a caller wants to STORE, and\n// returns the value as it will be stored, so that a body cannot store a\n// different string from the one that was checked.\nfunc ValidateSettingWrite(key, value string) (string, error) {\n\tif _, err := ValidateSettingKey(key); err != nil {\n\t\treturn \"\", err\n\t}\n\t// The prefix is owned by bd init --prefix, bd bootstrap and bd\n\t// rename-prefix. Refused HERE rather than at the front door because `bd\n\t// config set` is not the only door that reaches this plane: before this\n\t// role existed `bd config set-many issue_prefix=x` walked past the guard\n\t// and re-prefixed the workspace.\n\tif key == issueops.SettingKeyIssuePrefix || key == \"issue-prefix\" {\n\t\treturn \"\", fmt.Errorf(\"%w: %q is set by bd init --prefix, bd bootstrap or bd rename-prefix, not by a config write: \"+","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/workapi/workspaceconfig.go#L15-L51","documentation":"ValidateSettingKey rejects a config key that is empty or only whitespace, wrapping issueops.ErrValidation. A key must name something: an empty key is meaningless because the 'not set' answer it would produce corresponds to a question nobody asked. The key is returned unchanged when valid.","triggerScenarios":"Calling ValidateSettingKey(\"\") or ValidateSettingKey(\"   \") directly, or any code path through ValidateSettingWrite that passes a blank key (e.g. bd config set \"\" x, or a parsed config body with an empty key field).","commonSituations":"CLI/scripts building config set commands from shell variables that are unset or empty; JSON/YAML request bodies where the key field was omitted or left as an empty string; string splitting that produced an empty token (e.g. 'bd config set =value' splitting on '=').","solutions":["Supply a non-empty setting key (e.g. issue_prefix, status.custom) before calling the API","Trim and check the key in the caller: if strings.TrimSpace(key)==\"\" bail out with a clearer user-facing message","If the key comes from a variable or config file, verify the variable is populated and the parse didn't yield an empty token"],"exampleFix":"// before\nv, err := ValidateSettingWrite(key, value)\n// after\nif strings.TrimSpace(key) == \"\" {\n    return fmt.Errorf(\"--key is required, got %q\", key)\n}\nv, err := ValidateSettingWrite(key, value)","handlingStrategy":"validation","validationCode":"func validKey(k string) bool { return strings.TrimSpace(k) != \"\" }\nif !validKey(key) { return fmt.Errorf(\"config key must not be empty\") }","typeGuard":"func hasSettingKey(k string) bool { return strings.TrimSpace(k) != \"\" }","tryCatchPattern":"v, err := workapi.ValidateSettingWrite(key, val)\nif errors.Is(err, issueops.ErrValidation) { /* surface 'key required' to user */ }","preventionTips":["Never pass CLI/config variables to setting writes without checking they are non-empty","Trim keys before use and fail fast with a user-facing message","In config parsers, reject rows with empty key fields at parse time"],"tags":["config","validation","beads"],"backgroundTag":"empty-config-key","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}