gastownhall/beads · error

get dependency records: scan: %w

Error message

get dependency records: scan: %w

What it means

During row iteration in getDependencyRecordsIntoFromTable, scanDependencyRow failed to decode a row, so the loop closes the rows and aborts with this wrapped scan error. It means one row's column count/types/NULLs didn't match the dependency struct expectations in the per-issue query path — the query succeeded but a result row was unreadable.

Source

Thrown at internal/storage/issueops/dependency_queries.go:126

		batch := ids[start:end]
		placeholders := make([]string, len(batch))
		args := make([]any, len(batch))
		for i, id := range batch {
			placeholders[i] = "?"
			args[i] = id
		}
		rows, err := tx.QueryContext(ctx, fmt.Sprintf(
			`SELECT issue_id, %s AS depends_on_id, type, created_at, created_by, metadata, thread_id
			 FROM %s WHERE issue_id IN (%s) ORDER BY issue_id, depends_on_id, type, id`,
			DepTargetExpr, depTable, strings.Join(placeholders, ",")), args...)
		if err != nil {
			return fmt.Errorf("get dependency records from %s: %w", depTable, err)
		}
		for rows.Next() {
			dep, scanErr := scanDependencyRow(rows)
			if scanErr != nil {
				_ = rows.Close()
				return fmt.Errorf("get dependency records: scan: %w", scanErr)
			}
			result[dep.IssueID] = append(result[dep.IssueID], dep)
		}
		_ = rows.Close()
		if err := rows.Err(); err != nil {
			return fmt.Errorf("get dependency records: rows: %w", err)
		}
	}
	return nil
}

// GetDependentRecordsForIssuesInTx returns raw dependency rows keyed by TARGET
// id: for each id in targetIDs, the rows whose target is that id — its INCOMING
// edges, i.e. its dependents — spanning BOTH the durable and wisp dependency
// tables and applying NO type filter or visibility policy (the caller filters
// at hydration). It is the batched, target-keyed mirror of the source-keyed
// GetDependencyRecordsForIssuesInTx: one query per table per batch of
// queryBatchSize target ids, so cost is O(1 + N/queryBatchSize) round-trips per

View on GitHub (pinned to 71377f2769)

Solutions

  1. Read the wrapped scan error to find the failing column, then inspect the offending row in the named table.
  2. Harden scanDependencyRow for the observed NULL/type shape (sql.NullString etc.) — the shared scanner is the single fix point for both query paths.
  3. Bring rows written by an older schema forward through the driver's migration path instead of patching rows ad hoc.
Defensive patterns

Strategy: try-catch

Try / catch

recs, err := GetDependencyRecordsForIssuesInTx(ctx, tx, ids)
if err != nil {
    if strings.Contains(err.Error(), "get dependency records: scan") {
        // row-shape issue in the per-issue path; inspect the failing row/column
    }
    return err
}

Prevention

When it happens

Trigger: scanDependencyRow erroring on a row fetched by GetDependencyRecordsForIssuesInTx or GetDependencyRecordsForIssuesFromTableInTx — unexpected NULL in created_by/metadata/thread_id, or a driver type mismatch on created_at for rows in either dependency table.

Common situations: Mixed-version rows (older writer schema vs current scanner); driver upgrade changing scan type behavior; rows written by an external tool directly into the dependency tables.

Related errors


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