gastownhall/beads · error
touch dependency coordination: parent ID must not be empty
Error message
touch dependency coordination: parent ID must not be empty
What it means
TouchDependencyCoordinationTableInTx updates the freshness/coordination metadata cell for a dependency table and requires the parent issue ID to identify which shard/key to touch. An empty parent ID means the caller could not determine the owning issue, so the library refuses to write a meaningless coordination key. This is a defensive guard against callers bypassing required parameters.
Source
Thrown at internal/storage/issueops/dependency_coordination.go:35
// touchDependencyCoordinationInTx rewrites the coordination cells for both
// dependency tables in a fixed order. Writers that need a stable view of an
// issue's incoming parent-child edges use these cells to make concurrent Dolt
// transactions conflict rather than cell-merge.
func touchDependencyCoordinationInTx(ctx context.Context, tx DBTX, parentID string) error {
for _, tier := range [2]string{dependencyCoordinationDurableTier, dependencyCoordinationEphemeralTier} {
if err := TouchDependencyCoordinationTableInTx(ctx, tx, parentID, tier); err != nil {
return err
}
}
return nil
}
// TouchDependencyCoordinationTableInTx rewrites the coordination cell for one
// dependency table. table must be dependencies or wisp_dependencies.
func TouchDependencyCoordinationTableInTx(ctx context.Context, tx DBTX, parentID, table string) error {
if parentID == "" {
return fmt.Errorf("touch dependency coordination: parent ID must not be empty")
}
if table != dependencyCoordinationDurableTier && table != dependencyCoordinationEphemeralTier {
return fmt.Errorf("touch dependency coordination: unsupported table %q", table)
}
key := dependencyCoordinationKey(parentID, table)
if _, err := tx.ExecContext(ctx,
"REPLACE INTO local_metadata (`key`, value) VALUES (?, ?)", key, strconv.FormatInt(FreshRowLock(), 10)); err != nil {
return fmt.Errorf("touch dependency coordination for %s: %w", table, err)
}
return nil
}
func dependencyCoordinationKey(parentID, table string) string {
shard := dependencyCoordinationShard(parentID)
// A tier has 4096 shard rows: enough to keep unrelated writes apart while
// bounding the clone-local coordination state at 8192 rows. Same-parent
// operations always resolve to the same shard; a hash collision only adds a
// safe serialization conflict.View on GitHub (pinned to 71377f2769)
Solutions
- Fix the caller to resolve and pass a non-empty parent issue ID before opening the transaction.
- Add earlier validation (reject empty parent ID at the API entry point) to fail fast.
- If the parent ID should exist, check the input data — a missing/blank issue ID in the dependency record.
Example fix
// before
if err := TouchDependencyCoordinationTableInTx(ctx, tx, parentID, table); err != nil { ... }
// after
if parentID == "" {
return fmt.Errorf("cannot touch dependency coordination: empty parent ID")
}
if err := TouchDependencyCoordinationTableInTx(ctx, tx, parentID, table); err != nil { ... } Defensive patterns
Strategy: validation
Validate before calling
if parentID == "" {
return fmt.Errorf("parent ID required before touching dependency coordination")
}
err := TouchDependencyCoordinationTableInTx(ctx, tx, parentID, table) Type guard
func validParentID(id string) bool { return id != "" } Try / catch
if err := TouchDependencyCoordinationTableInTx(ctx, tx, parentID, table); err != nil {
return fmt.Errorf("touch coordination (parent=%q): %w", parentID, err)
} Prevention
- Validate the parent issue ID at the API entry point, before opening the transaction.
- Never construct coordination calls from values that may be zero-valued structs.
- Add a unit test for empty-parent-ID rejection in your persistence path.
When it happens
Trigger: Calling TouchDependencyCoordinationTableInTx with parentID="" — typically from a caller path (PersistDependenciesWithOptionsResult, addDependencyInTx, touchDependencyCoordinationInTx) where the parent issue ID was not resolved before the transaction ran.
Common situations: A bug in dependency persistence where an empty parent ID slips through earlier validation; hand-written code or tests invoking the internal touch helper without an ID; data corruption where the parent lookup returned empty.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- no store is open for this workspace
- touch dependency coordination: unsupported table %q
- not found
- no absolute native user directory is available
- ExternalDoltConfig: set either Socket OR (Host, Port), not b
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f74f7c6a697f0190.
Report an issue: GitHub.