gastownhall/beads · error

db: LabelSQLRepository.Insert %s/%s: %w

Error message

db: LabelSQLRepository.Insert %s/%s: %w

What it means

This error wraps any failure from the INSERT IGNORE statement in LabelSQLRepository.Insert, adding the issue ID and label for context. It is thrown when the underlying Dolt/SQL driver rejects the insert (connection problems, schema errors, constraint failures). The %w keeps the driver error chain intact for errors.Is/As checks.

Source

Thrown at internal/storage/domain/db/label.go:56

	}
	if label == "" {
		return fmt.Errorf("db: LabelSQLRepository.Insert: label must not be empty")
	}
	// Reject an over-length label before the INSERT IGNORE, which would otherwise
	// silently truncate it to the VARCHAR(255) column. This is the proxied-server
	// (uow) analog of issueops.AddLabelInTx's guard, so both write stacks return a
	// typed ErrFieldTooLong instead of storing a label the caller never sent.
	if err := types.CheckFieldLen("label", label); err != nil {
		return err
	}
	table := pickLabelTable(opts.UseWispsTable)
	//nolint:gosec // G201: table is one of two hardcoded constants
	result, err := r.runner.ExecContext(ctx,
		fmt.Sprintf("INSERT IGNORE INTO %s (issue_id, label) VALUES (?, ?)", table),
		issueID, label,
	)
	if err != nil {
		return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: %w", issueID, label, err)
	}
	rows, err := result.RowsAffected()
	if err != nil {
		return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: rows affected: %w", issueID, label, err)
	}
	if rows == 0 {
		issueTable := "issues"
		if opts.UseWispsTable {
			issueTable = "wisps"
		}
		var count int
		//nolint:gosec // G201: issueTable is one of two hardcoded constants.
		if err := r.runner.QueryRowContext(ctx, fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE id = ?", issueTable), issueID).Scan(&count); err != nil {
			return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: verify issue: %w", issueID, label, err)
		}
		if count == 0 {
			return fmt.Errorf("db: LabelSQLRepository.Insert %s/%s: issue does not exist", issueID, label)
		}

View on GitHub (pinned to 71377f2769)

Solutions

  1. Check the wrapped driver error with errors.As to see the root SQL error
  2. Verify the labels table exists via `bd doctor` or a fresh migration
  3. Confirm DB connectivity and that the connection is not closed
  4. Re-check label string for invalid characters or oversized length

Example fix

// before
err := repo.Insert(ctx, issueID, label, opts)
// after
if err := repo.Insert(ctx, issueID, label, opts); err != nil {
    var drv *mysql.MySQLError
    if errors.As(err, &drv) {
        log.Errorf("label insert driver error %d: %v", drv.Number, drv)
    }
    return err
}
Defensive patterns

Strategy: try-catch

Validate before calling

if issueID == "" || label == "" { return fmt.Errorf("issueID and label required") }
if err := db.PingContext(ctx); err != nil { return fmt.Errorf("db unavailable: %w", err) }

Type guard

func isSQLErr(err error) bool { var e *mysql.MySQLError; return errors.As(err, &e) }

Try / catch

if err := repo.Insert(ctx, issueID, label, opts); err != nil {
    var sqle *mysql.MySQLError
    if errors.As(err, &sqle) { log.Errorf("insert failed: %d %s", sqle.Number, sqle.Message) }
    return fmt.Errorf("add label %q to %s: %w", label, issueID, err)
}

Prevention

When it happens

Trigger: Calling LabelSQLRepository.Insert with a closed/failed DB connection, a malformed label exceeding column length, or a missing/locked `issue_labels` (or `wisp_labels`) table.

Common situations: Database not migrated to current schema version; connection dropped mid-request; attempting to insert into the wisps table path when UseWispsTable is set but the table does not exist.

Related errors


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