gastownhall/beads · error

marshaling metadata ref %q: %w

Error message

marshaling metadata ref %q: %w

What it means

After resolving a metadata ref to its minted ID, MergeMetadataRefs marshals the ID string to JSON (idJSON) for storage; a json.Marshal error is wrapped as 'marshaling metadata ref'.

Source

Thrown at internal/types/types.go:1478

// MergeMetadataRefs merges resolved metadata_refs into an issue's existing
// metadata JSON: each refs entry maps a metadata key to a plan-local node
// key, which is replaced with its minted ID from keyToID. Shared by the
// embedded and domain graph-apply paths.
func MergeMetadataRefs(existing json.RawMessage, refs map[string]string, keyToID map[string]string) (json.RawMessage, error) {
	merged := make(map[string]json.RawMessage, len(refs))
	if len(existing) > 0 {
		if err := json.Unmarshal(existing, &merged); err != nil {
			return nil, fmt.Errorf("re-parsing metadata: %w", err)
		}
	}
	for metaKey, refKey := range refs {
		resolvedID, ok := keyToID[refKey]
		if !ok {
			return nil, fmt.Errorf("metadata_ref %q references unknown key %q", metaKey, refKey)
		}
		idJSON, err := json.Marshal(resolvedID)
		if err != nil {
			return nil, fmt.Errorf("marshaling metadata ref %q: %w", metaKey, err)
		}
		merged[metaKey] = idJSON
	}
	return json.Marshal(merged)
}

// ParseWaitsForGateMetadata extracts the waits-for gate type from dependency metadata.
// Note: spawner identity comes from dependencies.depends_on_id in storage/query paths;
// metadata.spawner_id is parsed for compatibility/future explicit targeting.
// Returns WaitsForAllChildren on empty/invalid metadata for backward compatibility.
func ParseWaitsForGateMetadata(metadata string) string {
	if strings.TrimSpace(metadata) == "" {
		return WaitsForAllChildren
	}

	var meta WaitsForMeta
	if err := json.Unmarshal([]byte(metadata), &meta); err != nil {
		return WaitsForAllChildren

View on GitHub (pinned to 71377f2769)

Solutions

  1. Rebuild against stock beads to rule out a patched types package
  2. Check for custom marshaler overrides in the metadata path
  3. Report upstream with the inner error if reproducible on a clean build
Defensive patterns

Strategy: try-catch

Try / catch

merged, err := types.MergeMetadataRefs(existing, refs, keyToID)
if err != nil {
	if strings.Contains(err.Error(), "marshaling metadata ref") {
		log.Fatalf("metadata ref marshal failed: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: json.Marshal(resolvedID) returning an error inside MergeMetadataRefs.

Common situations: Unreachable in practice — marshaling a Go string to JSON cannot fail; would only occur with a non-standard build or a wrapper type with a faulty MarshalJSON.

Related errors


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