{"record":{"id":"05feaf34d1cdb396","repo":"gastownhall/beads","slug":"embeddeddolt-pin-connection-w-05feaf","errorCode":null,"errorMessage":"embeddeddolt: pin connection: %w","messagePattern":"embeddeddolt: pin connection: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/embeddeddolt/version_control.go","lineNumber":73,"sourceCode":"\t\treturn errClosed\n\t}\n\n\tvar db *sql.DB\n\tvar cleanup func() error\n\tdb, cleanup, err = OpenSQL(ctx, s.dataDir, s.database, s.branch)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\terr = errors.Join(err, cleanup())\n\t\t// Best-effort cleanup of orphaned tmp_pack_* files left by git\n\t\t// fetch in the Dolt git-remote-cache. Rate-limited internally.\n\t\ts.cleanGitRemoteCacheGarbage()\n\t}()\n\n\tconn, connErr := db.Conn(ctx)\n\tif connErr != nil {\n\t\treturn fmt.Errorf(\"embeddeddolt: pin connection: %w\", connErr)\n\t}\n\tdefer conn.Close()\n\n\treturn fn(conn)\n}\n\n// withMutatingDBConn is withDBConn for operations that mutate the database\n// or its version-control state (merge, push/pull, branch ops, backups, GC).\n// withDBConn runs outside any SQL transaction, so withConn's commit guard\n// never sees these — a read-only store satisfies the full DoltStorage\n// interface and must refuse them here instead (bd-578h9.12).\nfunc (s *EmbeddedDoltStore) withMutatingDBConn(ctx context.Context, fn func(db versioncontrolops.DBConn) error) error {\n\tif s.readOnly {\n\t\treturn ErrReadOnly\n\t}\n\treturn s.withDBConn(ctx, fn)\n}\n","sourceCodeStart":55,"sourceCodeEnd":91,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/embeddeddolt/version_control.go#L55-L91","documentation":"withPinnedDBConn could not obtain a dedicated connection from the embedded Dolt SQL database (db.Conn(ctx) failed), which is required to run the caller's function against one pinned connection. The pool could not hand out a connection — typically because the context was cancelled, the database is closed, or the pool is exhausted/broken. The error is wrapped with the embeddeddolt prefix so the underlying driver cause is preserved.","triggerScenarios":"Calling any path that goes through withMutatingPinnedDBConn (pinned-connection merge/pull/commit helpers) when the embedded SQL engine has been closed, when ctx is cancelled before the connection is acquired, or when the underlying driver cannot create a connection (corrupt database, resource exhaustion).","commonSituations":"A long-running `bd` command whose context deadline expired just before a pinned-connection operation; calling store methods after Close(); running operations concurrently beyond what the embedded engine tolerates; startup failures where the Dolt engine never initialized.","solutions":["Check the wrapped cause: if it is context.DeadlineExceeded/Canceled, increase the command's timeout or rerun without cancellation.","Ensure the store is still open — do not call merge/pull methods after Close(); in tests, defer close until after assertions.","Retry the operation once the engine is healthy; transient pool exhaustion clears when concurrent operations finish.","If the database itself failed to open (corruption), run `bd doctor` and restore/re-initialize the embedded database.","Serialize heavy VC operations (merge/pull/push) rather than running them concurrently against the same embedded store."],"exampleFix":"// before: assuming the store is always usable\nerr := store.Merge(ctx, branch, author)\n\n// after: guard context and store lifecycle\nif store.Closed() {\n    return errors.New(\"store closed\")\n}\nctx, cancel := context.WithTimeout(ctx, 2*time.Minute)\ndefer cancel()\nerr := store.Merge(ctx, branch, author)","handlingStrategy":"retry","validationCode":"if store.Closed() { // guard lifecycle before pinned-conn operations\n    return errors.New(\"store already closed\")\n}\nif err := ctx.Err(); err != nil {\n    return fmt.Errorf(\"context already done: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err := store.Merge(ctx, branch, author); err != nil {\n    if strings.Contains(err.Error(), \"pin connection\") {\n        if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n            // retry with a fresh, longer-lived context\n        }\n        // otherwise: store closed or engine unhealthy — do not retry blindly\n    }\n    return err\n}","preventionTips":["Keep contexts alive long enough for merge/pull operations; check ctx.Err() before long VC commands.","Never call store methods after Close(); in tests, close only after all assertions.","Avoid running many concurrent VC operations against one embedded store.","Re-initialize the store cleanly if the engine failed to open instead of reusing the broken instance."],"tags":["embeddeddolt","connection","context","pinned-connection"],"backgroundTag":"connection-acquire-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}