{"record":{"id":"f685d29d824771fb","repo":"gastownhall/beads","slug":"failed-to-commit-pending-changes-before-pull-w-f685d2","errorCode":null,"errorMessage":"failed to commit pending changes before pull: %w","messagePattern":"failed to commit pending changes before pull: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/store.go","lineNumber":3985,"sourceCode":"func (s *DoltStore) pullFromRemoteUnchecked(ctx context.Context, remote string) (retErr error) {\n\tctx, span := doltTracer.Start(ctx, \"dolt.pull\",\n\t\ttrace.WithSpanKind(trace.SpanKindClient),\n\t\ttrace.WithAttributes(append(s.doltSpanAttrs(),\n\t\t\tattribute.String(\"dolt.remote\", remote),\n\t\t\tattribute.String(\"dolt.branch\", s.branch),\n\t\t)...),\n\t)\n\tdefer func() { endSpan(span, retErr) }()\n\n\t// GH#2474: Auto-commit pending changes before pull to prevent\n\t// \"cannot merge with uncommitted changes\" errors. Store initialization\n\t// (schema init, molecule loading, metadata writes) can dirty the working\n\t// set before the user's pull command runs.\n\tif !s.readOnly {\n\t\tif err := s.commitBeforePull(ctx, \"auto-commit before pull\"); err != nil {\n\t\t\t// \"nothing to commit\" is fine — working set is already clean\n\t\t\tif !isDoltNothingToCommit(err) {\n\t\t\t\treturn fmt.Errorf(\"failed to commit pending changes before pull: %w\", err)\n\t\t\t}\n\t\t}\n\t}\n\n\t// bd-6dnrw.3: capture the pre-pull commit of the branch this store reads so a\n\t// successful merge can recompute the denormalized is_blocked column for the\n\t// rows it changed. Read before the transport; an unreadable head degrades to\n\t// a full recompute.\n\t//\n\t// ga-ivaps Finding 3: read this unconditionally, including for read-only\n\t// stores. verifyPullLanded's cheap fast path — a head that moved is proof the\n\t// transport landed — needs it, and without it every pull that DID merge\n\t// something pays a network DOLT_FETCH round trip it could have skipped. (A\n\t// no-op pull moves no head and refreshes the tracking ref regardless, so the\n\t// saved round trip is on the merged pulls, never the no-op ones.)\n\t// recomputeBlockedAfterPull below still runs only for writable stores.\n\t//\n\t// ga-ivaps Finding 1 (attempt 2): read the tip of s.branch, not the session","sourceCodeStart":3967,"sourceCodeEnd":4003,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/store.go#L3967-L4003","documentation":"DoltStore auto-commits any pending working-set changes before running a pull, because a merge cannot proceed with uncommitted changes. This error wraps whatever failure occurred during that pre-pull commit, excluding the benign 'nothing to commit' case. It means the store could not get its working set clean before the merge, so the pull was aborted.","triggerScenarios":"Calling Pull()/PullRemote() on a writable store whose working set is dirty (store init writes schema/molecule/metadata rows) and where the commit fails for a real reason: storage I/O failure, lock contention, context cancellation, or a Dolt commit error that is not 'nothing to commit'.","commonSituations":"Another process holds the Dolt database lock; the sql-server connection dropped mid-commit; disk full; a corrupted working set from a prior crashed merge; calling pull from a store that just wrote metadata and hit a transient MySQL timeout.","solutions":["Read the wrapped cause (%w) to identify the actual commit failure (timeout, lock, I/O).","Re-run the pull — most causes (transient timeouts, lock contention) are temporary.","Check that no other process/CLI is holding the Dolt database or working set lock.","Verify the database server is reachable and the disk is not full.","Run `bd doctor` / inspect dolt_status to see what rows are dirty and why."],"exampleFix":"// before\nerr := store.Pull(ctx)\n// after\nif err != nil && strings.Contains(err.Error(), \"failed to commit pending changes before pull\") {\n    time.Sleep(retryDelay) // transient lock/timeout cause\n    err = store.Pull(ctx)\n}","handlingStrategy":"retry","validationCode":"if writable, err := store.CanCommit(ctx); err != nil { /* resolve dirty state before pulling */ }","typeGuard":null,"tryCatchPattern":"if err := store.Pull(ctx); err != nil {\n    if strings.Contains(err.Error(), \"failed to commit pending changes before pull\") {\n        // inspect wrapped cause; wait and retry once\n        time.Sleep(time.Second)\n        err = store.Pull(ctx)\n    }\n    return err\n}","preventionTips":["Avoid holding external locks on the Dolt working set across pull calls.","Check disk space and server reachability before sync operations.","Keep pull contexts alive long enough for the pre-commit to finish.","Run bd doctor if pulls repeatedly fail at the commit stage."],"tags":["dolt","storage","pull","commit"],"backgroundTag":"pull-precommit-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}