gastownhall/beads · error

invalid metadata: %w

Error message

invalid metadata: %w

What it means

The metadata value supplied to updateIssueInTx failed storage.NormalizeMetadataValue, so it cannot be serialized into the metadata column. The underlying normalization error is wrapped as "invalid metadata: %w".

Source

Thrown at internal/storage/issueops/update.go:442

	for key, value := range updates {
		if !IsAllowedUpdateField(key) {
			return nil, fmt.Errorf("invalid field for update: %s", key)
		}

		columnName := key
		if key == "wisp" {
			columnName = "ephemeral"
		}
		setClauses = append(setClauses, fmt.Sprintf("`%s` = ?", columnName))

		// Handle JSON serialization for array fields stored as TEXT.
		if key == "waiters" {
			waitersJSON, _ := json.Marshal(value)
			args = append(args, string(waitersJSON))
		} else if key == "metadata" {
			metadataStr, err := storage.NormalizeMetadataValue(value)
			if err != nil {
				return nil, fmt.Errorf("invalid metadata: %w", err)
			}
			args = append(args, metadataStr)
		} else {
			args = append(args, value)
		}
	}

	// Auto-clear pinned column when status transitions away from "pinned".
	if rawStatus, ok := updates["status"]; ok {
		var statusStr string
		switch v := rawStatus.(type) {
		case string:
			statusStr = v
		case types.Status:
			statusStr = string(v)
		}
		if oldIssue.Pinned && statusStr != string(types.StatusPinned) {
			if _, alreadySet := updates["pinned"]; !alreadySet {

View on GitHub (pinned to 71377f2769)

Solutions

  1. Convert metadata to map[string]string before assigning it to updates["metadata"].
  2. Inspect the wrapped error (errors.Unwrap / %w chain) for the specific normalization failure.
  3. Marshal then unmarshal through map[string]string to coerce values: convert each value with fmt.Sprintf if needed.

Example fix

// before
updates["metadata"] = rawMetadata // map[string]interface{}
// after
m := map[string]string{}
for k, v := range rawMetadata {
    m[k] = fmt.Sprintf("%v", v)
}
updates["metadata"] = m
Defensive patterns

Strategy: validation

Validate before calling

if raw, ok := updates["metadata"]; ok {
    if _, err := storage.NormalizeMetadataValue(raw); err != nil {
        return fmt.Errorf("metadata must normalize: %w", err)
    }
}

Type guard

func asStringMap(v interface{}) (map[string]string, bool) {
    m, ok := v.(map[string]string)
    return m, ok
}

Try / catch

if _, err := storage.UpdateIssue(ctx, id, updates, actor); err != nil {
    if strings.Contains(err.Error(), "invalid metadata") {
        return fmt.Errorf("fix metadata shape (map[string]string): %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: Passing a metadata value that is not a map[string]string (e.g. nested maps, non-string values, nil of wrong shape) or a JSON string that does not normalize cleanly when calling UpdateIssueInTx with key "metadata".

Common situations: JSON-decoded metadata containing numbers/bools instead of strings; callers passing structs rather than string maps; mixing metadata formats between versions of the schema.

Related errors


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