{"record":{"id":"4f397a3b649d5f0b","repo":"gastownhall/beads","slug":"acquire-connection-for-merge-w","errorCode":null,"errorMessage":"acquire connection for merge: %w","messagePattern":"acquire connection for merge: %w","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/dolt/store.go","lineNumber":4917,"sourceCode":"\tctx, span := doltTracer.Start(ctx, \"dolt.merge_with_strategy\",\n\t\ttrace.WithSpanKind(trace.SpanKindClient),\n\t\ttrace.WithAttributes(append(s.doltSpanAttrs(),\n\t\t\tattribute.String(\"dolt.merge_branch\", branch),\n\t\t\tattribute.String(\"dolt.merge_strategy\", strategy),\n\t\t)...),\n\t)\n\tdefer func() { endSpan(span, retErr) }()\n\n\tpreHead := \"\"\n\tif !s.readOnly {\n\t\tif h, err := s.GetCurrentCommit(ctx); err == nil {\n\t\t\tpreHead = h\n\t\t}\n\t}\n\n\tconn, err := s.db.Conn(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"acquire connection for merge: %w\", err)\n\t}\n\tconflicts, err = versioncontrolops.MergeWithStrategy(ctx, conn, branch, s.commitAuthorString(), strategy)\n\t// Release the pinned connection before the recompute: s.db's pool can be\n\t// configured with a single connection (setupTestStore's MaxOpenConns: 1\n\t// mirrors constrained production configs), and recomputeBlockedAfterPull\n\t// acquires its own connection — held past this point, conn would starve\n\t// it of the only one available.\n\tcloseErr := conn.Close()\n\tif len(conflicts) > 0 {\n\t\tspan.SetAttributes(attribute.Int(\"dolt.conflicts\", len(conflicts)))\n\t}\n\tif err != nil {\n\t\treturn conflicts, err\n\t}\n\tif closeErr != nil {\n\t\treturn conflicts, fmt.Errorf(\"release merge connection: %w\", closeErr)\n\t}\n\tif !s.readOnly {","sourceCodeStart":4899,"sourceCodeEnd":4935,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/dolt/store.go#L4899-L4935","documentation":"MergeWithStrategy needs a dedicated connection pinned for the Dolt merge session. This error wraps a failure from s.db.Conn(ctx) when acquiring that connection. Pool exhaustion is the notable trigger here — the surrounding code even documents that recomputeBlockedAfterPull needs its own connection and the pool may be MaxOpenConns:1.","triggerScenarios":"Calling the store's strategy merge (bd vc merge --strategy) when s.db.Conn(ctx) fails: another operation (often a still-pinned conn or an in-flight recompute) holds the sole connection, or ctx expires while queued for the pool.","commonSituations":"Test stores configured with MaxOpenConns:1 mirroring constrained production; concurrent merge and recompute operations; Dolt server unresponsive so pooled conns are dead.","solutions":["Close/commit all other connections, txs, and rows before merging.","Follow the codebase's own pattern: release the conn (conn.Close()) before running recomputeBlockedAfterPull.","Increase MaxOpenConns so merge and recompute can each hold a connection.","Check ctx deadlines if the pool wait is being cancelled.","Confirm the Dolt server is healthy; retry to evict stale pooled connections."],"exampleFix":"// before\nconn, err := s.db.Conn(ctx)\nif err != nil { return nil, fmt.Errorf(\"acquire connection for merge: %w\", err) }\nconflicts, err = versioncontrolops.MergeWithStrategy(ctx, conn, ...)\n// recompute runs while conn still held -> starvation\n// after\nconflicts, err := func() ([]Conflict, error) {\n    conn, err := s.db.Conn(ctx)\n    if err != nil { return nil, fmt.Errorf(\"acquire connection for merge: %w\", err) }\n    defer conn.Close()\n    return versioncontrolops.MergeWithStrategy(ctx, conn, ...)\n}()\nif err != nil { return conflicts, err }\n// now recompute can acquire its own connection","handlingStrategy":"validation","validationCode":"stats := db.Stats()\nif stats.MaxOpenConnections > 0 && stats.InUse >= stats.MaxOpenConnections {\n    return errors.New(\"cannot merge: connection pool exhausted; release pinned connections first\")\n}\nif err := ctx.Err(); err != nil { return err }","typeGuard":null,"tryCatchPattern":"conn, err := s.db.Conn(ctx)\nif err != nil {\n    if errors.Is(err, context.DeadlineExceeded) {\n        return fmt.Errorf(\"merge starved waiting for connection (pool=%d in use)\", db.Stats().InUse)\n    }\n    return fmt.Errorf(\"acquire connection for merge: %w\", err)\n}","preventionTips":["Close the merge connection before recomputeBlockedAfterPull (as the codebase comment mandates)","Avoid MaxOpenConns:1 with multi-connection workflows; test configs should mirror production sizing","Always defer conn.Close() right after a successful acquire","Alert on pool wait metrics to catch starvation early"],"tags":["dolt","merge","connection-pool","database"],"backgroundTag":"connection-acquisition-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}