{"record":{"id":"a994e42479563f58","repo":"tursodatabase/turso","slug":"database-is-invalid","errorCode":null,"errorMessage":"database is invalid","messagePattern":"database is invalid","errorType":"exception","errorClass":"NullReferenceException","httpStatus":null,"severity":"error","filePath":"bindings/dotnet/src/Turso.Raw/Public/Handles/TursoDatabaseHandle.cs","lineNumber":40,"sourceCode":"        if (_database != IntPtr.Zero)\n            TursoInterop.DatabaseDeinit(_database);\n\n        if (_ownerReferenceAdded)\n        {\n            _owner!.DangerousRelease();\n            _ownerReferenceAdded = false;\n        }\n\n        handle = IntPtr.Zero;\n        _database = IntPtr.Zero;\n        _owner = null;\n        return true;\n    }\n\n    public void ThrowIfInvalid()\n    {\n        if (IsInvalid)\n            throw new NullReferenceException(\"database is invalid\");\n    }\n\n    public static TursoDatabaseHandle FromPtrs(IntPtr database, IntPtr connection)\n    {\n        var handle = new TursoDatabaseHandle();\n        handle._database = database;\n        handle.SetHandle(connection);\n        return handle;\n    }\n\n    public static TursoDatabaseHandle FromConnectionPtr(IntPtr connection, SafeHandle owner)\n    {\n        ArgumentNullException.ThrowIfNull(owner);\n        if (connection == IntPtr.Zero)\n            throw new ArgumentException(\"Connection pointer must not be null.\", nameof(connection));\n        if (owner.IsInvalid || owner.IsClosed)\n            throw new ObjectDisposedException(nameof(owner));\n","sourceCodeStart":22,"sourceCodeEnd":58,"githubUrl":"https://github.com/tursodatabase/turso/blob/c1e59287258d99b309e362a63f48822256e2f65f/bindings/dotnet/src/Turso.Raw/Public/Handles/TursoDatabaseHandle.cs#L22-L58","documentation":"TursoDatabaseHandle wraps two native pointers (database and connection). ThrowIfInvalid throws NullReferenceException when either is zero, which happens when the handle was released (ReleaseHandle nulls both after DatabaseDeinit) or never fully initialized. It guards every TursoBindings entry point (PrepareStatement, RegisterScalarFunction, LoadExtension, and so on).","triggerScenarios":"Using a database handle after Close()/Dispose() finalized it; reusing a handle stored in a field after another path disposed it; a partially constructed handle because OpenDatabase failed mid-way; DI container disposing a shared database while requests still run.","commonSituations":"Use-after-dispose in scoped services; caches that outlive the database; shutdown paths racing in-flight statements; storing the handle statically and recreating the database on config reload without updating the reference.","solutions":["Check handle.IsInvalid before each use and reopen when true.","Scope ownership: create the handle with `using`/single owner, and have all consumers reference that owner instead of the raw handle.","After reopening, re-prepare statements — they belong to the old connection.","For encrypted databases, remember a failed Open due to a wrong key also leaves you without a usable handle; surface that error instead of reusing the handle."],"exampleFix":"// before\ndb.Close();\nvar stmt = TursoBindings.PrepareStatement(db, sql); // NullReferenceException: database is invalid\n\n// after\ndb.Close();\ndb = TursoBindings.OpenDatabase(path);\nvar stmt = TursoBindings.PrepareStatement(db, sql);","handlingStrategy":"type-guard","validationCode":"// check before every use of the database handle\nif (db is null || db.IsInvalid)\n    db = TursoBindings.OpenDatabase(path);","typeGuard":"static bool IsUsable(TursoDatabaseHandle db) => db is not null && !db.IsInvalid;","tryCatchPattern":"try { TursoBindings.PrepareStatement(db, sql); }\ncatch (NullReferenceException) when (db.IsInvalid) { db = TursoBindings.OpenDatabase(path); /* then retry */ }","preventionTips":["Give the database handle a single owner and a lifetime that encloses all statements.","Never capture the raw handle in long-lived caches; hold the owning object instead.","On config reload or Close, null out every reference so stale use fails fast as null rather than as an invalid pointer."],"tags":["dotnet","native-interop","handle-lifecycle","use-after-dispose"],"backgroundTag":"use-after-dispose","analyzedSha":"c1e59287258d99b309e362a63f48822256e2f65f","analyzedAt":"2026-08-20T07:02:18.389Z","contentChangedAt":"2026-08-20T07:02:18.389Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}