gastownhall/beads · error
deferred parents: %s/%s: %w
Error message
deferred parents: %s/%s: %w
What it means
Wraps a QueryContext failure from one of the four deferred-parent edge queries in descendantsOfFutureDeferredParents, identified by "depTable/issueTable". Missing optional wisp tables are skipped by design, but a failure on any edge naming a required table (e.g. `dependencies`/`issues`) aborts the deferred-children computation. This strictness exists so ready work never returns an incomplete exclusion set with a nil error.
Source
Thrown at internal/storage/domain/db/ready_work.go:115
SELECT dep.issue_id
FROM %s dep
JOIN %s parent ON parent.id = dep.%s
WHERE dep.type = 'parent-child'
AND parent.defer_until IS NOT NULL
AND parent.defer_until > UTC_TIMESTAMP()
`, e.depTable, e.issueTable, e.targetCol)
rows, err := r.runner.QueryContext(ctx, q)
if err != nil {
// Each edge joins a dependency table to an issue table. Only one of
// the four pairs is entirely durable, but three of them name at
// least one required table. Classified by error class alone this
// swallowed the edges naming a missing dependencies or issues and
// let the rest answer, returning an incomplete set of deferred
// children with a nil error.
if missingOptionalWispTable(err) {
continue
}
return nil, fmt.Errorf("deferred parents: %s/%s: %w", e.depTable, e.issueTable, err)
}
if err := scanStringsInto(rows, &childIDs); err != nil {
return nil, fmt.Errorf("deferred parents: %s/%s: %w", e.depTable, e.issueTable, err)
}
}
return childIDs, nil
}
func scanStringsInto(rows *sql.Rows, out *[]string) error {
defer func() { _ = rows.Close() }()
for rows.Next() {
var s string
if err := rows.Scan(&s); err != nil {
return fmt.Errorf("scan: %w", err)
}
*out = append(*out, s)
}
return rows.Err()View on GitHub (pinned to 71377f2769)
Solutions
- Inspect the wrapped error and the depTable/issueTable pair named in the message
- Ensure required tables (dependencies, issues) exist and are readable
- If it's a wisp table, check that dberrors/missingOptionalWispTable classification matches your engine's error text (version alignment)
- Retry on transient connectivity failures
Defensive patterns
Strategy: try-catch
Validate before calling
// ensure required dependency tables exist before ready work
for _, t := range []string{"dependencies", "issues"} {
var c int
if err := db.QueryRow("SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = ?", t).Scan(&c); err != nil || c == 0 {
return fmt.Errorf("missing required table %s", t)
}
} Type guard
func isDeferredEdgeError(err error) bool {
return err != nil && strings.Contains(err.Error(), "deferred parents: ") && strings.Contains(err.Error(), "/")
} Try / catch
ready, err := repo.GetReadyWork(ctx, filter)
if isDeferredEdgeError(err) {
// check the depTable/issueTable named in the message; migrate schema, then retry
}
Prevention
- Keep dependencies/issues tables present via migrations
- Align engine and driver versions so missing-wisp-table errors are classified correctly
- Watch for schema drift between environments
- Retry only on transient connectivity errors
When it happens
Trigger: Calling ready work with IncludeDeferred=false when a future deferred parent exists and the edge query `dependencies JOIN issues` (or a wisp-pair edge that is not classified as an optional missing wisp table) fails to execute.
Common situations: Schema drift removing or renaming `dependencies`; a wisp-table error that isn't recognized as the optional-missing-table class (version mismatch between code classification and engine error text); connectivity failures.
Related errors
- get ready work: compute deferred parent children: %w
- get parent descendants: %w
- deferred parents: check %s: %w
- scan: %w
- ErrTransaction
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/508f891f50ac1761.
Report an issue: GitHub.