gastownhall/beads · error

%q is set by bd init --prefix, bd bootstrap or bd rename-pre

Error message

%q is set by bd init --prefix, bd bootstrap or bd rename-prefix, not by a config write: storing it here would leave existing ids under the old prefix with nothing to reconcile them

What it means

ValidateSettingWrite refuses to store the issue-prefix setting under either spelling (issue_prefix or issue-prefix), wrapping issueops.ErrValidation. The prefix is owned exclusively by bd init --prefix, bd bootstrap, and bd rename-prefix; storing it via a plain config write would leave existing IDs under the old prefix with nothing to reconcile them. The guard sits here rather than at the CLI front door because config set-many previously bypassed it.

Source

Thrown at internal/workapi/workspaceconfig.go:51

		return "", fmt.Errorf("%w: config key must not be empty", issueops.ErrValidation)
	}
	return key, nil
}

// ValidateSettingWrite checks a key and value a caller wants to STORE, and
// returns the value as it will be stored, so that a body cannot store a
// different string from the one that was checked.
func ValidateSettingWrite(key, value string) (string, error) {
	if _, err := ValidateSettingKey(key); err != nil {
		return "", err
	}
	// The prefix is owned by bd init --prefix, bd bootstrap and bd
	// rename-prefix. Refused HERE rather than at the front door because `bd
	// config set` is not the only door that reaches this plane: before this
	// role existed `bd config set-many issue_prefix=x` walked past the guard
	// and re-prefixed the workspace.
	if key == issueops.SettingKeyIssuePrefix || key == "issue-prefix" {
		return "", fmt.Errorf("%w: %q is set by bd init --prefix, bd bootstrap or bd rename-prefix, not by a config write: "+
			"storing it here would leave existing ids under the old prefix with nothing to reconcile them",
			issueops.ErrValidation, key)
	}
	// status.custom is PROJECTED into custom_statuses, which reads consult
	// first, so a value that cannot be projected must not become a row.
	// Checking here rather than leaving it to SyncCustomStatusesTable is what
	// makes the refusal a validation error rather than a storage failure.
	if key == issueops.SettingKeyStatusCustom && value != "" {
		if _, err := types.ParseCustomStatusConfig(value); err != nil {
			return "", fmt.Errorf("%w: invalid %s value: %v", issueops.ErrValidation, key, err)
		}
	}
	return value, nil
}

// FilterSettingsEnumeration takes the rows a store handed back and returns the
// ones the settings enumeration is allowed to carry: everything except the KV
// plane.

View on GitHub (pinned to 71377f2769)

Solutions

  1. Use bd rename-prefix to change the prefix so existing IDs are reconciled
  2. For new workspaces use bd init --prefix or bd bootstrap instead of a config write
  3. Remove issue_prefix/issue-prefix from any config set/set-many scripts and write only non-protected keys

Example fix

// before
bd config set issue_prefix=NEW
// after
bd rename-prefix NEW
Defensive patterns

Strategy: validation

Validate before calling

var protectedKeys = map[string]bool{"issue_prefix": true, "issue-prefix": true}
if protectedKeys[key] { return fmt.Errorf("use bd rename-prefix to change %s", key) }

Type guard

func isPrefixKey(k string) bool { return k == "issue_prefix" || k == "issue-prefix" }

Try / catch

_, err := workapi.ValidateSettingWrite(key, val)
if errors.Is(err, issueops.ErrValidation) && isPrefixKey(key) { /* route to bd rename-prefix flow */ }

Prevention

When it happens

Trigger: Calling ValidateSettingWrite("issue_prefix", x) or ValidateSettingWrite("issue-prefix", x), e.g. via bd config set issue_prefix=NEW or bd config set-many issue_prefix=x.

Common situations: Trying to rename a workspace prefix by hand through config instead of bd rename-prefix; copy-pasted setup scripts from before the dedicated commands existed; automation that writes all settings wholesale including the prefix.

Related errors


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