gastownhall/beads · error

invalid %s value: %v

Error message

invalid %s value: %v

What it means

ValidateSettingWrite validates status.custom values by parsing them with types.ParseCustomStatusConfig before the value can be stored, wrapping issueops.ErrValidation. status.custom is projected into the custom_statuses table that readers consult first, so a value that cannot be projected must never become a row. Checking here makes the refusal a clean validation error rather than a later storage failure.

Source

Thrown at internal/workapi/workspaceconfig.go:61

		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.
//
// THE KV PLANE RIDES IN THE SAME TABLE AND IS NOT SETTINGS. Generic `bd kv`
// keys and the `bd remember` memories nested under them are USER DATA stored as
// config rows beneath kvkeys.Prefix, and an enumeration that returned them
// published that data on `bd config list` and on GET /v0/beads/config alike —
// the latter reachable by anything a shared bearer admits, if one is
// configured at all, and redacting on the KEY NAME while a memory's content is
// in the VALUE. `bd config list` carrying kv rows was never a
// design; it fell out of one storage table holding two planes.
//

View on GitHub (pinned to 71377f2769)

Solutions

  1. Validate the value locally with types.ParseCustomStatusConfig(value) and fix the structure before storing
  2. Use the canonical JSON shape for custom statuses (check the parser for required fields)
  3. Prefer dedicated commands for managing custom statuses rather than raw config writes where available

Example fix

// before
bd config set status.custom='{"label":"in-progress"}'
// after
bd config set status.custom='{"label":"in-progress","count":null,"order":0}'
Defensive patterns

Strategy: validation

Validate before calling

if _, err := types.ParseCustomStatusConfig(value); err != nil {
    return fmt.Errorf("status.custom is not valid: %v", err)
}

Type guard

func validCustomStatus(v string) bool { _, err := types.ParseCustomStatusConfig(v); return err == nil }

Try / catch

_, err := workapi.ValidateSettingWrite("status.custom", val)
if errors.Is(err, issueops.ErrValidation) { /* show parse error, do not retry as-is */ }

Prevention

When it happens

Trigger: Calling ValidateSettingWrite("status.custom", <malformed value>) with a non-empty value that ParseCustomStatusConfig rejects, e.g. bd config set status.custom='not-json' or a structurally invalid custom-status config.

Common situations: Hand-editing status.custom values with typos or wrong JSON structure; migrating configs between schema versions where the custom-status format changed; shell quoting mangling the JSON before it reaches the API.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


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