gastownhall/beads · error

serializing waits-for also_blocks metadata: %w

Error message

serializing waits-for also_blocks metadata: %w

What it means

After adding also_blocks=true to WaitsForMeta, NewWaitsForBlockingDependency re-serializes the struct with json.Marshal; a marshal error is wrapped with this message and returned.

Source

Thrown at internal/types/types.go:1434

// metadata so waitsForGateBlockedSQL additionally blocks while the spawner
// itself is open, not only while it has an open parent-child child. Use this
// instead of NewWaitsForDependency exactly when the caller is collapsing a
// would-be DepBlocks edge (from needs/depends_on) into this waits-for edge
// because the two would otherwise collide on the same (source, target) pair
// — never for a plain waits_for with no matching needs/depends_on entry.
func NewWaitsForBlockingDependency(issueID, spawnerID, gate string) (*Dependency, error) {
	dep, err := NewWaitsForDependency(issueID, spawnerID, gate)
	if err != nil {
		return nil, err
	}
	var meta WaitsForMeta
	if err := json.Unmarshal([]byte(dep.Metadata), &meta); err != nil {
		return nil, fmt.Errorf("parsing waits-for metadata to set also_blocks: %w", err)
	}
	meta.AlsoBlocks = true
	raw, err := json.Marshal(meta)
	if err != nil {
		return nil, fmt.Errorf("serializing waits-for also_blocks metadata: %w", err)
	}
	dep.Metadata = string(raw)
	return dep, nil
}

// NewGraphNodeDependency builds the dependency record for a graph-plan node's
// inline dep, shared by the embedded and domain apply paths so their
// resolution semantics cannot drift: an empty type defaults to blocks, and
// the target resolves as a plan-local key first, then as a literal issue ID.
// Waits-for deps carry gate metadata like waits-for edges (all-children
// default, no explicit spawner).
func NewGraphNodeDependency(issueID string, depType DependencyType, target string, keyToID map[string]string) (*Dependency, error) {
	if depType == "" {
		depType = DepBlocks
	}
	targetID := keyToID[target]
	if targetID == "" {
		targetID = target

View on GitHub (pinned to 71377f2769)

Solutions

  1. Rebuild the binary to rule out a patched types package
  2. Check for a custom MarshalJSON on WaitsForMeta and remove it
  3. File an upstream issue with the inner wrapped error if it reproduces on a stock build
Defensive patterns

Strategy: try-catch

Try / catch

dep, err := types.NewWaitsForBlockingDependency(issueID, spawnerID, gate)
if err != nil {
	if strings.Contains(err.Error(), "serializing waits-for also_blocks") {
		log.Fatalf("also_blocks metadata serialization failed: %v", err)
	}
	return err
}

Prevention

When it happens

Trigger: json.Marshal(meta) failing after meta.AlsoBlocks was set to true inside NewWaitsForBlockingDependency.

Common situations: Theoretically unreachable: WaitsForMeta holds only string and bool fields which always marshal successfully; would imply a non-standard build or custom marshaler.

Related errors


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