gastownhall/beads · error

import chunk %d/%d failed, %d issues already committed (comm

Error message

import chunk %d/%d failed, %d issues already committed (committed rows are durable; re-run the import to resume — it converges): %w

What it means

In the classic (chunked) import, issue rows are written in chunks of importChunkSize, each committing independently. If a chunk's CreateIssuesWithFullOptions fails, chunks before it are already durably committed. The error reports which chunk failed, how many issues were already committed, and instructs the user that re-running the import converges — committed rows are deduplicated/updated, and remaining rows get written.

Source

Thrown at cmd/bd/import_shared.go:388

		if targetChunk, inBatch := firstChunkOf[dep.DependsOnID]; inBatch && targetChunk > rowChunk {
			later = append(later, dep)
			continue
		}
		inline = append(inline, dep)
	}
	return inline, later
}

// writeImportRowChunks writes the ordered rows (dependencies already narrowed to
// their inline subset) in bounded transactions, pausing between commits.
func writeImportRowChunks(ctx context.Context, store storage.DoltStorage, ordered []*types.Issue, actor string, rowOpts storage.BatchCreateOptions, pacer *importChunkPacer) error {
	total := len(ordered)
	chunks := (total + importChunkSize - 1) / importChunkSize
	for start, chunk := 0, 1; start < total; start, chunk = start+importChunkSize, chunk+1 {
		end := min(start+importChunkSize, total)
		pacer.beforeTx()
		if err := store.CreateIssuesWithFullOptions(ctx, ordered[start:end], actor, rowOpts); err != nil {
			return fmt.Errorf("import chunk %d/%d failed, %d issues already committed (committed rows are durable; re-run the import to resume — it converges): %w", chunk, chunks, start, err)
		}
		fmt.Fprintf(importProgress, "bd import: %d/%d issues committed\n", end, total) //nolint:gosec // G705: stderr, not a browser context
	}
	return nil
}

// wireDeferredImportDeps applies the deferred edges once every target row exists,
// without rewriting the rows themselves. rowTotal is the count of phase-1 rows
// already committed, used only for the resume message.
func wireDeferredImportDeps(ctx context.Context, store storage.DoltStorage, deferred []deferredImportEdges, phase1Stale map[string]struct{}, rowTotal int, actor string, opts storage.BatchCreateOptions, pacer *importChunkPacer) error {
	depRows := make([]*types.Issue, 0, len(deferred))
	for _, d := range deferred {
		if _, stale := phase1Stale[d.issue.ID]; stale {
			continue // stale snapshot: its deps stay out too (bd-578h9.8)
		}
		cp := *d.issue
		cp.Dependencies = d.deps
		// The row landed in phase 1 and its labels/comments merged there;

View on GitHub (pinned to 71377f2769)

Solutions

  1. Simply re-run the same import — already-committed rows are durable and the import converges (idempotent upsert semantics).
  2. Fix the underlying storage condition indicated by the wrapped error (e.g. close competing `bd` processes holding the SQLite lock, free disk space).
  3. If a specific row repeatedly fails, narrow it down by bisecting the JSONL file and inspect the offending record for invalid fields.
  4. Increase patience/pacing or reduce concurrency against the DB if lock contention is chronic.

Example fix

// before
bd import big.jsonl   # import chunk 4/10 failed, 300 issues already committed ...
// after
# close other bd sessions / fix disk, then just:
bd import big.jsonl   # resumes, converges on committed rows
Defensive patterns

Strategy: retry

Validate before calling

// pre-flight: ensure no competing writer holds the DB
if f, err := os.OpenFile(dbPath, os.O_RDWR, 0o644); err != nil {
    return fmt.Errorf("database appears locked or unavailable: %w", err)
} else { f.Close() }

Try / catch

if err := importIssuesChunked(ctx, store, issues, actor, opts); err != nil {
    var chunkErr error
    if strings.Contains(err.Error(), "already committed") {
        // partial progress is durable; re-running converges
        chunkErr = retry(2, time.Second, func() error {
            return importIssuesChunked(ctx, store, issues, actor, opts)
        })
    }
    return chunkErr
}

Prevention

When it happens

Trigger: `bd import` of a large JSONL on the classic storage path where a mid-import chunk write fails: SQLite write-lock contention, disk full, storage error, constraint violation on specific rows, or process interruption isn't possible here (this is an explicit error return).

Common situations: SQLite database locked by another bd process while importing hundreds of issues; transient disk I/O errors; Dolt server hiccup mid-import; one bad row with a constraint problem in a later chunk.

Related errors


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