{"record":{"id":"2b8833a8dd5d6aab","repo":"gastownhall/beads","slug":"compact-step-q-w","errorCode":null,"errorMessage":"compact step %q: %w","messagePattern":"compact step %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/versioncontrolops/compact.go","lineNumber":39,"sourceCode":"//\n// conn must be a single database connection (not a pooled *sql.DB) since the\n// stored procedures rely on session-scoped state (current branch, working set).\nfunc Compact(ctx context.Context, conn DBConn, initialHash, boundaryHash string, oldCommits int, recentHashes []string) (retErr error) {\n\tbranchCreated := false\n\n\t// Best-effort cleanup: if any step fails after creating the temp branch,\n\t// try to return to main and delete the temp branch so future compactions\n\t// aren't blocked by a leftover branch.\n\tdefer func() {\n\t\tif retErr != nil && branchCreated {\n\t\t\t_, _ = conn.ExecContext(ctx, \"CALL DOLT_CHECKOUT('main')\")\n\t\t\t_, _ = conn.ExecContext(ctx, \"CALL DOLT_BRANCH('-D', 'compact-tmp')\")\n\t\t}\n\t}()\n\n\texecSQL := func(name, query string, args ...interface{}) error {\n\t\tif _, err := conn.ExecContext(ctx, query, args...); err != nil {\n\t\t\treturn fmt.Errorf(\"compact step %q: %w\", name, err)\n\t\t}\n\t\treturn nil\n\t}\n\n\tif err := execSQL(\"create temp branch\", \"CALL DOLT_BRANCH('compact-tmp', ?)\", boundaryHash); err != nil {\n\t\treturn err\n\t}\n\tbranchCreated = true\n\n\tif err := execSQL(\"checkout temp\", \"CALL DOLT_CHECKOUT('compact-tmp')\"); err != nil {\n\t\treturn err\n\t}\n\tif err := execSQL(\"soft reset to initial\", \"CALL DOLT_RESET('--soft', ?)\", initialHash); err != nil {\n\t\treturn err\n\t}\n\tmsg := fmt.Sprintf(\"compact: squash %d commits into base snapshot\", oldCommits)\n\tif err := execSQL(\"commit squashed base\", \"CALL DOLT_COMMIT('-Am', ?)\", msg); err != nil {\n\t\treturn err","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/versioncontrolops/compact.go#L21-L57","documentation":"Compact's internal execSQL helper wraps every step (branch creation, checkout, reset, squashed commit, cherry-picks, resets) as 'compact step \"<step name>\": %w', pinpointing which stage of the squash recipe failed. The wrapper is generic — the underlying driver error and step name together identify the cause. A deferred best-effort cleanup removes the compact-tmp branch if a later step fails after branch creation.","triggerScenarios":"Calling versioncontrolops.Compact when any step fails: creating 'compact-tmp' when it already exists (leftover from a prior crash), cherry-picking a commit that conflicts or is empty without --allow-empty, DOLT_RESET on a bad hash, or connection loss mid-recipe.","commonSituations":"A previous failed Compact left the compact-tmp branch behind; cherry-picking recent commits whose changes conflict with the squashed base; corrupt/invalid initialHash or boundaryHash; session contention because Compact requires a dedicated single connection, not a pool.","solutions":["Read the quoted step name in the error and address that specific step (e.g. 'create temp branch' → leftover compact-tmp; 'cherry-pick <hash>' → conflict)","Delete any leftover 'compact-tmp' branch and checkout main before retrying Compact","Verify initialHash/boundaryHash/recentHashes are valid commit hashes (dolt log)","Run Compact on a single dedicated connection (not pooled *sql.DB) since steps depend on session state","Inspect the wrapped driver error for conflict details and resolve before cherry-picks"],"exampleFix":"// before\n_, err := db.ExecContext(ctx, \"CALL DOLT_BRANCH('-D', 'compact-tmp')\") // only on failure path\nerr := versioncontrolops.Compact(ctx, conn, init, boundary, n, hashes)\n// after\n// idempotent pre-cleanup before retrying\n_, _ = conn.ExecContext(ctx, \"CALL DOLT_CHECKOUT('main')\")\n_, _ = conn.ExecContext(ctx, \"CALL DOLT_BRANCH('-D', 'compact-tmp')\")\nerr := versioncontrolops.Compact(ctx, conn, init, boundary, n, hashes)","handlingStrategy":"try-catch","validationCode":"var tmp int\n_ = conn.QueryRowContext(ctx,\n    \"SELECT COUNT(*) FROM dolt_branches WHERE name = 'compact-tmp'\").Scan(&tmp)\nif tmp > 0 {\n    _, _ = conn.ExecContext(ctx, \"CALL DOLT_CHECKOUT('main')\")\n    _, _ = conn.ExecContext(ctx, \"CALL DOLT_BRANCH('-D', 'compact-tmp')\")\n}\n// also verify boundaryHash exists in dolt_log before Compact","typeGuard":null,"tryCatchPattern":"err := versioncontrolops.Compact(ctx, conn, init, boundary, n, hashes)\nif err != nil {\n    var stepName string\n    if m := regexp.MustCompile(`compact step \"([^\"]+)\"`).FindStringSubmatch(err.Error()); m != nil {\n        stepName = m[1] // route to step-specific recovery (e.g. recreate temp branch, resolve cherry-pick conflict)\n    }\n    cleanupTempBranch(ctx, conn)\n    return fmt.Errorf(\"compact failed at step %q: %w\", stepName, err)\n}","preventionTips":["Clean up leftover 'compact-tmp' branches before running Compact","Run Compact on a single dedicated connection (session state matters)","Validate that initial/boundary/recent hashes exist in the log","Run PruneRemoteRefs and DoltGC after Compact as documented","Avoid concurrent sessions mutating history during compaction"],"tags":["dolt","compact","history","cherry-pick"],"backgroundTag":"dolt-compact-step-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}