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

  1. Inspect the wrapped error and the depTable/issueTable pair named in the message
  2. Ensure required tables (dependencies, issues) exist and are readable
  3. If it's a wisp table, check that dberrors/missingOptionalWispTable classification matches your engine's error text (version alignment)
  4. 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

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


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