gastownhall/beads · error

failed to batch-fetch epic issues: %w

Error message

failed to batch-fetch epic issues: %w

What it means

After mapping epics to children, the function bulk-loads the epic issues themselves via GetIssuesByIDsInTx. A failure there is wrapped as 'failed to batch-fetch epic issues'. It means the final step of building epicIssueMap failed, so closure decisions cannot include full issue details.

Source

Thrown at internal/storage/issueops/epic_closure.go:116

						return nil, fmt.Errorf("scan child status: %w", err)
					}
					childStatusMap[id] = status
				}
				statusRows.Close()
			}
		}
	}

	// Step 4: Batch-fetch all epic issues
	epicsWithChildren := make([]string, 0)
	for _, epicID := range epicIDs {
		if len(epicChildMap[epicID]) > 0 {
			epicsWithChildren = append(epicsWithChildren, epicID)
		}
	}
	epicIssues, err := GetIssuesByIDsInTx(ctx, tx, epicsWithChildren, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to batch-fetch epic issues: %w", err)
	}
	epicIssueMap := make(map[string]*types.Issue, len(epicIssues))
	for _, issue := range epicIssues {
		epicIssueMap[issue.ID] = issue
	}

	// Step 5: Build results from cached data
	var results []*types.EpicStatus
	for _, epicID := range epicIDs {
		children := epicChildMap[epicID]
		if len(children) == 0 {
			continue
		}

		issue, ok := epicIssueMap[epicID]
		if !ok || issue == nil {
			continue
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Retry the command; the wrapped cause from GetIssuesByIDsInTx names the real failure.
  2. Check DB connectivity and server logs for mid-query disconnects.
  3. Run migrations if the wrapped error mentions unknown tables/columns.
  4. Upgrade bd if the wrapped error indicates an internal query bug.
Defensive patterns

Strategy: retry

Validate before calling

if err := db.PingContext(ctx); err != nil { return fmt.Errorf("db unavailable: %w", err) }
if len(epicIDs) == 0 { return nil } // avoid odd empty-batch paths

Type guard

func isEpicBatchFetchErr(err error) bool {
    return err != nil && strings.Contains(err.Error(), "failed to batch-fetch epic issues:")
}

Try / catch

epics, err := GetEpicsEligibleForClosureInTx(ctx, tx)
if err != nil {
    if isEpicBatchFetchErr(err) && isTransient(err) {
        return retryWithBackoff(ctx, 3)
    }
    return err
}

Prevention

When it happens

Trigger: Calling GetEpicsEligibleForClosureInTx when GetIssuesByIDsInTx fails for the collected epic IDs — internal query error, connection drop, or the issues table missing expected columns.

Common situations: The same connection dying mid-function after earlier queries succeeded; schema drift in the issues table; an internal bug/regression in GetIssuesByIDsInTx on unusual ID sets (e.g. empty list edge cases).

Understand the failure class

Background: "query failed", "%w: SQL error" — wrapped database query errors in Go libraries explained — this error's family across 3 libraries.

Related errors


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