gastownhall/beads · error
create: marshal waits-for meta: %w
Error message
create: marshal waits-for meta: %w
What it means
Thrown when types.NewWaitsForDependency fails while marshaling the waits-for gate metadata into a dependency during issue creation. Waits-for encodes a spawner ID and a gate condition into dependency metadata; marshaling fails if the gate metadata is invalid or cannot be serialized. It is a pre-insert validation failure, not a storage failure.
Source
Thrown at internal/storage/domain/issue.go:1051
}
if spec.SwapDirection {
dep.IssueID, dep.DependsOnID = dep.DependsOnID, dep.IssueID
}
depSourceIsWisp, err := u.isWispID(ctx, dep.IssueID)
if err != nil {
return result, fmt.Errorf("create: determine dep source tier for %s: %w", dep.IssueID, err)
}
if err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: depSourceIsWisp}); err != nil {
return result, fmt.Errorf("create: add dep %s -> %s: %w", dep.IssueID, dep.DependsOnID, err)
}
result.PostCreateWrites = true
}
if params.WaitsFor != nil {
// Spawner identity is the depends_on_id; metadata carries the gate.
dep, err := types.NewWaitsForDependency(issue.ID, params.WaitsFor.SpawnerID, params.WaitsFor.Gate)
if err != nil {
return result, fmt.Errorf("create: marshal waits-for meta: %w", err)
}
if err := u.depRepo.Insert(ctx, dep, actor, DepInsertOpts{UseWispsTable: useWisp}); err != nil {
return result, fmt.Errorf("create: add waits-for: %w", err)
}
result.PostCreateWrites = true
}
return result, nil
}
func validateExplicitIDPrefix(id, prefix, allowedPrefixes string) error {
if strings.HasPrefix(id, prefix+"-") {
return nil
}
for _, allowed := range strings.Split(allowedPrefixes, ",") {
allowed = strings.TrimSpace(allowed)
if allowed != "" && strings.HasPrefix(id, allowed+"-") {
return nilView on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped error from NewWaitsForDependency for the exact marshal/validation cause
- Ensure the Gate value is JSON-serializable metadata (strings, numbers, maps)
- Provide a valid, non-empty SpawnerID
- Build the dependency via types.NewWaitsForDependency yourself beforehand to fail fast before Create
Example fix
// before: non-serializable gate value
params.WaitsFor = &types.WaitsFor{SpawnerID: parent.ID, Gate: func() bool { return true }}
// after: JSON-serializable gate metadata
params.WaitsFor = &types.WaitsFor{SpawnerID: parent.ID, Gate: map[string]any{"status": "done"}} Defensive patterns
Strategy: validation
Validate before calling
if params.WaitsFor != nil {
if params.WaitsFor.SpawnerID == "" {
return fmt.Errorf("waits-for requires a spawner ID")
}
if _, err := json.Marshal(params.WaitsFor.Gate); err != nil {
return fmt.Errorf("gate not serializable: %w", err)
}
} Try / catch
_, err := uc.Create(ctx, params, actor)
if err != nil && strings.Contains(err.Error(), "marshal waits-for meta") {
// rebuild WaitsFor with a serializable gate
params.WaitsFor.Gate = normalizeGate(params.WaitsFor.Gate)
} Prevention
- Only use JSON-serializable values for Gate metadata
- Call types.NewWaitsForDependency yourself first to fail fast
- Never leave SpawnerID empty
- Avoid storing functions/channels/time.Time without formatting in gates
When it happens
Trigger: Calling Create with params.WaitsFor set where NewWaitsForDependency returns an error — typically an invalid/unserializable Gate value or a missing/invalid SpawnerID.
Common situations: Programmatically constructing WaitsFor with a gate object containing unsupported types (channels, funcs, non-JSON-serializable values); passing an empty spawner ID after a refactor.
Related errors
- create: add waits-for: %w
- serializing waits-for metadata: %w
- serializing waits-for also_blocks metadata: %w
- no store is open for this workspace
- not found
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/c88f85ac419726b3.
Report an issue: GitHub.