{"record":{"id":"97c9eaf66b2ddd54","repo":"tursodatabase/turso","slug":"cannot-access-a-disposed-object-object-name-ag","errorCode":null,"errorMessage":"Cannot access a disposed object.\r\nObject name: 'AggregateInvocation'.","messagePattern":"Cannot access a disposed object\\.\r\nObject name: 'AggregateInvocation'\\.","errorType":"exception","errorClass":"ObjectDisposedException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Data.Sqlite/SqliteConnection.Aggregates.cs","lineNumber":80,"sourceCode":"    private static object? InvokeSeededAggregateStep<TAccumulate>(Func<TAccumulate, object?[], TAccumulate> function, object? accumulator, object?[] args)\n        => function((TAccumulate)accumulator!, args);\n\n    private static object? InvokeResultSelector<TAccumulate, TResult>(Func<TAccumulate, TResult> resultSelector, object? accumulator)\n        => resultSelector((TAccumulate)accumulator!);\n\n    private static IntPtr InitializeAggregate(IntPtr context)\n    {\n        var registration = (AggregateFunctionRegistration?)GCHandle.FromIntPtr(context).Target\n            ?? throw new ObjectDisposedException(nameof(AggregateFunctionRegistration));\n        return registration.CreateInvocationHandle();\n    }\n\n    private static TursoExtensionValue StepAggregate(IntPtr context, IntPtr aggregateContext, int argc, IntPtr argv)\n    {\n        try\n        {\n            var invocation = (AggregateInvocation?)GCHandle.FromIntPtr(aggregateContext).Target\n                ?? throw new ObjectDisposedException(nameof(AggregateInvocation));\n            invocation.Step(ReadArguments(argc, argv));\n            return CreateResult(null);\n        }\n        catch (SqliteException ex)\n        {\n            return CreateError(\"__turso_sqlite_error__:\" + ex.SqliteErrorCode.ToString(System.Globalization.CultureInfo.InvariantCulture) + \":\" + ex.Message);\n        }\n        catch (Exception ex)\n        {\n            return CreateError(ex.Message);\n        }\n    }\n\n    private static TursoExtensionValue FinalizeAggregate(IntPtr context, IntPtr aggregateContext)\n    {\n        try\n        {\n            var invocation = (AggregateInvocation?)GCHandle.FromIntPtr(aggregateContext).Target","sourceCodeStart":62,"sourceCodeEnd":98,"githubUrl":"https://github.com/tursodatabase/turso/blob/6c7252267988c76e632af00a671e4b9788dfae13/bindings/dotnet/src/Turso.Data.Sqlite/SqliteConnection.Aggregates.cs#L62-L98","documentation":"The native side invoked the step callback of a custom aggregate function (CreateAggregate), but the GCHandle pointing to the managed AggregateInvocation no longer has a target: the invocation object was disposed while the statement was still executing. This is a use-after-dispose across the native/managed interop boundary, surfaced as ObjectDisposedException('AggregateInvocation'). It almost always means the connection (or the registration's native context) was closed/freed before the query consuming the aggregate finished.","triggerScenarios":"Calling connection.Close() or Dispose() while a reader iterating a query that uses a custom aggregate is still open; a 'using var conn' scope ending before deferred LINQ evaluation over an open reader runs; one thread disposing the connection while another executes the aggregate; statements left un-disposed that are stepped after connection teardown.","commonSituations":"Returning IQueryable/deferred sequences from a repository that owns the connection in a using block, fire-and-forget tasks racing connection disposal, and sharing one connection across async flows where the first to finish disposes it.","solutions":["Keep the connection alive until every reader/command using the custom aggregate has completed and been disposed.","Materialize results (ToList()/ToArray()) before the owning connection's using scope exits.","If multi-threaded, give each logical unit its own SqliteConnection instead of disposing a shared one.","Audit that CreateAggregate registrations outlive every statement that references the aggregate."],"exampleFix":"// before\nIEnumerable<Row> rows;\nusing (var conn = new SqliteConnection(cs))\n{\n    conn.Open();\n    conn.CreateAggregate<long, long>(\"total\", (acc, x) => acc + x);\n    using var cmd = new SqliteCommand(\"SELECT total(v) FROM t\", conn);\n    rows = ReadRows(cmd.ExecuteReader()); // deferred\n} // conn disposed while reader still steps -> ObjectDisposedException\n\n// after\nusing (var conn = new SqliteConnection(cs))\n{\n    conn.Open();\n    conn.CreateAggregate<long, long>(\"total\", (acc, x) => acc + x);\n    using var cmd = new SqliteCommand(\"SELECT total(v) FROM t\", conn);\n    rows = ReadRows(cmd.ExecuteReader()).ToList(); // fully consumed inside the scope\n}","handlingStrategy":"validation","validationCode":"static SqliteDataReader ExecuteFully(SqliteCommand cmd)\n{\n    if (cmd.Connection?.State != ConnectionState.Open)\n        throw new InvalidOperationException(\"Connection must be open before executing an aggregate query.\");\n    return cmd.ExecuteReader(); // caller MUST dispose reader inside the connection's lifetime\n}","typeGuard":null,"tryCatchPattern":"try\n{\n    var result = ReadAggregateQuery(conn);\n}\ncatch (ObjectDisposedException ex) when (ex.ObjectName is \"AggregateInvocation\" or \"AggregateFunctionRegistration\")\n{\n    // Connection was torn down mid-query: restart on a fresh connection instead of reusing state.\n    throw new InvalidOperationException(\"Query outlived its connection; re-run on a new connection.\", ex);\n}","preventionTips":["Materialize query results (ToList/ToArray) before disposing the owning connection.","Wrap reader and command in 'using' inside the connection's 'using' so finalization happens while alive.","Never dispose a connection from another thread to cancel a query; use command cancellation.","Register aggregates per connection and treat the registration's lifetime as the connection's lifetime."],"tags":["ado-net","sqlite","turso","aggregates","user-defined-functions","use-after-dispose","interop"],"backgroundTag":"use-after-dispose","analyzedSha":"6c7252267988c76e632af00a671e4b9788dfae13","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}