gastownhall/beads · error

importing config %q: %w

Error message

importing config %q: %w

What it means

Wraps the error from SetConfigInTx while writing each config entry (key/value, e.g. memories) inside the import transaction. The issue-count check passed (database is empty), but persisting a config row failed — SQL error such as a missing/corrupt config table, constraint violation, or connection/context failure. Because this happens inside the import transaction, the whole import transaction is rolled back, leaving the database still empty and the import safely retryable.

Source

Thrown at internal/storage/embeddeddolt/store.go:750

	issues []*types.Issue,
	configEntries map[string]string,
	actor string,
) (int, error) {
	var imported int
	err := s.withConn(ctx, true, func(tx *sql.Tx) error {
		// Atomically check: is the database empty?
		stats := &types.Statistics{}
		if err := issueops.ScanIssueCountsInTx(ctx, tx, stats); err != nil {
			return fmt.Errorf("checking issue count: %w", err)
		}
		if stats.TotalIssues > 0 {
			return nil // database is not empty — skip import
		}

		// Import config entries (memories, etc.)
		for key, value := range configEntries {
			if err := issueops.SetConfigInTx(ctx, tx, key, value); err != nil {
				return fmt.Errorf("importing config %q: %w", key, err)
			}
		}

		if len(issues) == 0 {
			return nil
		}

		// Auto-detect prefix from first issue if not already provided
		if _, hasPrefix := configEntries["issue_prefix"]; !hasPrefix {
			firstPrefix := utils.ExtractIssuePrefix(issues[0].ID)
			if firstPrefix != "" {
				if err := issueops.SetConfigInTx(ctx, tx, "issue_prefix", firstPrefix); err != nil {
					return fmt.Errorf("setting issue_prefix: %w", err)
				}
			}
		}

		// Create all issues in the same transaction

View on GitHub (pinned to 71377f2769)

Solutions

  1. Apply migrations first ('bd migrate' or open once via bd) so the config table exists, then retry.
  2. Inspect the wrapped key (%q) and inner SQL error to find the offending config entry.
  3. Retry the import — transactional rollback keeps the DB empty, so a rerun is idempotent.
  4. Sanitize/canonicalize config keys in the source export if a specific key is malformed.

Example fix

// before
entries := map[string]string{"issue_prefix": "bd", "issue_prefix ": "xx"} // duplicate/odd key
store.ImportIssues(ctx, issues, entries)

// after
delete(entries, "issue_prefix ") // remove conflicting key
store.ImportIssues(ctx, issues, entries)
Defensive patterns

Strategy: retry

Validate before calling

for key := range configEntries {
    if strings.TrimSpace(key) == "" || strings.ContainsAny(key, "\n\r") {
        return fmt.Errorf("invalid config key %q", key)
    }
}

Try / catch

err := store.ImportIssues(ctx, issues, cfg)
if err != nil && strings.Contains(err.Error(), "importing config") {
    // rollback already happened; fix offending key per error text, then retry
    return store.ImportIssues(ctx, issues, sanitizeConfig(cfg))
}

Prevention

When it happens

Trigger: Import path with configEntries where: (1) the config table doesn't exist (unmigrated schema); (2) a duplicate-key or constraint error on the config key; (3) ctx cancelled mid-loop; (4) transaction connection lost after several writes.

Common situations: Importing into a freshly created but unmigrated database; importing a .beads/issues.jsonl export whose config keys collide with an existing (partially imported) dataset; long imports cancelled by CI timeouts.

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/17a9659c07a2c72c. Report an issue: GitHub.