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
- Simply re-run the same import — already-committed rows are durable and the import converges (idempotent upsert semantics).
- Fix the underlying storage condition indicated by the wrapped error (e.g. close competing `bd` processes holding the SQLite lock, free disk space).
- If a specific row repeatedly fails, narrow it down by bisecting the JSONL file and inspect the offending record for invalid fields.
- 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
- Don't run parallel `bd` write commands during a large import.
- Re-run the same import after a chunk failure — it converges by design.
- Ensure adequate disk space before multi-thousand-row imports.
- Watch the stderr progress lines (`N/M issues committed`) to gauge resume state.
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
- import dependency pass chunk %d/%d failed (all %d issue rows
- check existing issues before import: %w
- failed to remove database: %w
- import failed: %w
- dry-run: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d51c3fd0fcec75da.
Report an issue: GitHub.