gastownhall/beads · error

metadata validation failed for issue %s: %w

Error message

metadata validation failed for issue %s: %w

What it means

PrepareIssueForInsert validates the issue's optional Metadata field against the configured metadata schema via ValidateMetadataIfConfigured before insert, wrapping failures as "metadata validation failed for issue %s: %w" with the issue ID. Metadata validation only runs when a schema is configured; unstructured metadata passes untouched when none is set.

Source

Thrown at internal/storage/issueops/create.go:531

			affectedIssues, affectedWisps, err = AffectedByDepChangeInTx(ctx, tx, dependency.source, dependency.target, dependency.depType)
		}
		if err != nil {
			return nil, nil, fmt.Errorf("affected by created dependency %s -> %s: %w", dependency.source, dependency.target, err)
		}
		for _, id := range affectedIssues {
			add(id, false)
		}
		for _, id := range affectedWisps {
			add(id, true)
		}
	}
	return issueIDs, wispIDs, nil
}

// PrepareIssueForInsert normalizes timestamps, validates, and computes the content hash.
func PrepareIssueForInsert(issue *types.Issue, customStatuses, customTypes []string) error {
	if err := ValidateMetadataIfConfigured(issue.Metadata); err != nil {
		return fmt.Errorf("metadata validation failed for issue %s: %w", issue.ID, err)
	}

	// Normalize timestamps to UTC, defaulting to now.
	now := time.Now().UTC()
	if issue.CreatedAt.IsZero() {
		issue.CreatedAt = now
	} else {
		issue.CreatedAt = issue.CreatedAt.UTC()
	}
	if issue.UpdatedAt.IsZero() {
		issue.UpdatedAt = now
	} else {
		issue.UpdatedAt = issue.UpdatedAt.UTC()
	}

	// Ensure closed issues have a closed_at timestamp.
	if issue.Status == types.StatusClosed && issue.ClosedAt == nil {
		maxTime := issue.CreatedAt

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped validation error to see which metadata key/rule failed and correct the issue's Metadata map before insert.
  2. Run the same validation client-side (ValidateMetadataIfConfigured) before submitting so the failure surfaces at the call site.
  3. If legacy issues legitimately lack new fields, relax the configured schema (make fields optional) or backfill the metadata.

Example fix

// before
issue := &types.Issue{Title: "x", Metadata: map[string]any{"priority": "high"}}
// schema expects integer priority -> validation fails

// after
issue := &types.Issue{Title: "x", Metadata: map[string]any{"priority": 2}}
if err := issueops.ValidateMetadataIfConfigured(issue.Metadata); err != nil {
	return fmt.Errorf("fix metadata: %w", err)
}
Defensive patterns

Strategy: validation

Validate before calling

if err := issueops.ValidateMetadataIfConfigured(issue.Metadata); err != nil {
	return fmt.Errorf("metadata for %s invalid: %w", issue.ID, err)
}

Try / catch

if err := store.CreateIssue(ctx, issue); err != nil {
	if strings.Contains(err.Error(), "metadata validation failed") {
		return fmt.Errorf("reject/repair metadata before retry: %w", err)
	}
	return err
}

Prevention

When it happens

Trigger: Creating/updating an issue (CreateIssueInTxWithResult, PromoteFromEphemeralInTx, PreparePublicCreateRequest) whose Metadata map violates the configured schema: missing required keys, wrong value types, or disallowed fields; importing issues from another repo whose metadata does not match local config.

Common situations: Teams adding a metadata schema to an existing database whose old issues (or import payloads) predate it; automation writing metadata keys with wrong JSON types (string vs number); typo'd metadata keys in scripts.

Related errors


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