gastownhall/beads · error

scan dependent: %w

Error message

scan dependent: %w

What it means

This is the lowest-level decode error in dependency_queries.go: scanDependentRow failed to Scan a dependency row's eight columns into a types.Dependency struct (with sql.NullTime/NullString for nullable fields). All higher-level dependent-record and count errors funnel through it, so it always indicates a row-content or schema-shape problem, not a connection problem.

Source

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

		if scanErr != nil {
			return nil, fmt.Errorf("get dependent records: scan: %w", scanErr)
		}
		deps = append(deps, dep)
	}
	return deps, rows.Err()
}

// scanDependentRow scans a dependents row that INCLUDES the row id (the keyset
// cursor). The shared scanDependencyRow does not select id, and adding it there
// would ripple through every source-keyed read, so the target-keyed read owns
// this variant.
func scanDependentRow(rows *sql.Rows) (*types.Dependency, error) {
	var dep types.Dependency
	var createdAt sql.NullTime
	var metadata, threadID sql.NullString

	if err := rows.Scan(&dep.ID, &dep.IssueID, &dep.DependsOnID, &dep.Type, &createdAt, &dep.CreatedBy, &metadata, &threadID); err != nil {
		return nil, fmt.Errorf("scan dependent: %w", err)
	}
	if createdAt.Valid {
		dep.CreatedAt = createdAt.Time
	}
	if metadata.Valid {
		dep.Metadata = metadata.String
	}
	if threadID.Valid {
		dep.ThreadID = threadID.String
	}
	return &dep, nil
}

// CountDependentRecordsInTx returns the number of DISTINCT inbound edges of
// targetID, applying the same sargable target predicate and optional depType
// filter as GetDependentRecordsInTx but no keyset/limit. Callers that want a
// true total membership count need it without paging to exhaustion. Like the
// paged read it is a RAW count spanning both tables; the caller applies any

View on GitHub (pinned to 71377f2769)

Solutions

  1. Parse the wrapped error: it names the column/conversion that failed (e.g. 'converting NULL to string is unsupported').
  2. Locate the bad row by id prefix of the failing table and repair or delete it.
  3. Re-run or hand-apply pending migrations to match expected schema (id, issue_id, depends_on columns, type, created_at, created_by, metadata, thread_id).
  4. If corruption is suspected, restore from backup or re-init the database.
  5. Keep nullable columns in scans as sql.Null* when the schema permits NULL.

Example fix

// before: schema missing thread_id column after partial migration
SELECT id, issue_id, issue_id AS depends_on_id, type, created_at, created_by, metadata FROM dependencies;
// after: complete migration adds the column
ALTER TABLE dependencies ADD COLUMN thread_id VARCHAR(64) NULL;
Defensive patterns

Strategy: validation

Validate before calling

// Verify the table shape matches the scanner's expectation (8 columns, right names)
cols, _ := tx.QueryContext(ctx, "SELECT * FROM dependencies LIMIT 1")
ts, _ := cols.ColumnTypes()
// expect: id, issue_id, depends_on target column(s), type, created_at, created_by, metadata, thread_id
if len(ts) < 8 { /* migration incomplete; repair before reading */ }

Type guard

func scanDependentSafe(rows *sql.Rows) (dep *types.Dependency, err error) {
    defer func() { if r := recover(); r != nil { err = fmt.Errorf("scan dependent: %v", r) } }()
    return scanDependentRow(rows)
}

Try / catch

dep, err := scanDependentRow(rows)
if err != nil {
    // unwrap to find failing column: errors.Is/As on the driver error, then repair schema/data
    log.Printf("bad dependency row in %s: %v", table, err)
}

Prevention

When it happens

Trigger: Any read path (GetDependentRecordsForIssuesInTx, GetDependentRecordsInTx) encountering a row whose column count or types don't match: NULL issue_id/depends_on_id/type/id, non-time created_at, schema with missing or reordered columns.

Common situations: Partial migrations leaving tables with old column sets; manual data fixes that NULLed required columns; database restored from an incompatible version; corrupted storage files.

Related errors


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