{"record":{"id":"e529e0e82269582c","repo":"HangfireIO/Hangfire","slug":"disabletransactionscope-option-does-not-support","errorCode":null,"errorMessage":"`DisableTransactionScope` option does not support external queue providers","messagePattern":"`DisableTransactionScope` option does not support external queue providers","errorType":"exception","errorClass":"NotSupportedException","httpStatus":null,"severity":"error","filePath":"src/Hangfire.SqlServer/SqlServerWriteOnlyTransaction.cs","lineNumber":246,"sourceCode":"            var persistentQueue = provider.GetJobQueue();\n\n            if (persistentQueue.GetType() == typeof(SqlServerJobQueue))\n            {\n                var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].JobQueue (JobId, Queue) values (@jobId, @queue)\");\n\n                AddCommand(_queueCommands, queue, batch => batch.Create(query)\n                    .AddParameter(\"@jobId\", long.Parse(jobId, CultureInfo.InvariantCulture), DbType.Int64)\n                    .AddParameter(\"@queue\", queue, DbType.String));\n\n                _queuesToSignal.Add(queue);\n            }\n            else\n            {\n#if FEATURE_TRANSACTIONSCOPE\n                if (_storage.Options.DisableTransactionScope)\n                {\n                    throw new NotSupportedException($\"`{nameof(SqlServerStorageOptions.DisableTransactionScope)}` option does not support external queue providers\");\n                }\n#endif\n                _queueCommandQueue.Enqueue((connection, transaction) => persistentQueue.Enqueue(\n                    connection,\n#if !FEATURE_TRANSACTIONSCOPE\n                    transaction,\n#endif\n                    queue,\n                    jobId));\n            }\n        }\n\n        public override void IncrementCounter(string key)\n        {\n            if (key == null) throw new ArgumentNullException(nameof(key));\n\n            var query = _storage.GetQueryFromTemplate(static schemaName =>\n$@\"insert into [{schemaName}].Counter ([Key], [Value]) values (@key, @value)\");","sourceCodeStart":228,"sourceCodeEnd":264,"githubUrl":"https://github.com/HangfireIO/Hangfire/blob/c236dd0f930f831ec151e436e138ddc429a02a72/src/Hangfire.SqlServer/SqlServerWriteOnlyTransaction.cs#L228-L264","documentation":"NotSupportedException thrown in SqlServerWriteOnlyTransaction.AddToQueue (SqlServerWriteOnlyTransaction.cs:246), compiled only under FEATURE_TRANSACTIONSCOPE (.NET Framework). It fires when the target queue is served by an external/non-default IPersistentJobQueueProvider whose queue type is not SqlServerJobQueue, AND SqlServerStorageOptions.DisableTransactionScope is true. External queue providers rely on TransactionScope for ambient-transaction enlistment; DisableTransactionScope disables that mechanism, so enqueuing through them is unsupported.","triggerScenarios":"On .NET Framework: set options.DisableTransactionScope = true, register a custom or MSMQ-based queue provider, and enqueue a job to a queue that resolves to that provider. AddToQueue sees persistentQueue.GetType() != typeof(SqlServerJobQueue) and throws.","commonSituations":"A team enables DisableTransactionScope to fix abandoned locks or connection-pool exhaustion (as the option's doc remarks describe), while still routing some queues through MSMQ or a custom provider; upgrading/migrating from MSMQ queues without removing the option.","solutions":["Do not enable DisableTransactionScope when using external/custom queue providers; leave it false (the default) on .NET Framework.","Route the affected queues through the default SqlServerJobQueueProvider so the SQL-based enqueue path is used instead of the external path.","Move to the .NET Core / .NET 5+ build, where FEATURE_TRANSACTIONSCOPE is not defined and TransactionScope is already not used, eliminating the conflict.","Remove the external queue provider registration if it is no longer needed."],"exampleFix":"// before (.NET Framework, MSMQ provider registered)\nvar options = new SqlServerStorageOptions { DisableTransactionScope = true };\nglobalConfig.UseSqlServerStorage(connStr, options);\n// queues routed to MSMQ -> throws on AddToQueue\n// after\nvar options = new SqlServerStorageOptions(); // DisableTransactionScope left false\nglobalConfig.UseSqlServerStorage(connStr, options);","handlingStrategy":"validation","validationCode":"// Before startup, on .NET Framework, reject the incompatible combination.\n#if FEATURE_TRANSACTIONSCOPE\nvar usesExternalProvider = storage.QueueProviders.ContainsNonSqlServerProvider();\nif (options.DisableTransactionScope && usesExternalProvider)\n{\n    throw new InvalidOperationException(\"DisableTransactionScope cannot be used with external queue providers; remove the option or the provider.\");\n}\n#endif","typeGuard":null,"tryCatchPattern":null,"preventionTips":["On .NET Framework, never combine DisableTransactionScope = true with MSMQ or custom queue providers.","Prefer the default SqlServerJobQueueProvider for all queues when DisableTransactionScope is enabled.","Consider the .NET Core/.NET 5+ build where TransactionScope is not used and the conflict disappears.","Document which queues are SQL-backed before turning on DisableTransactionScope."],"tags":["configuration","transactionscope","queue-provider","net-framework","notsupportedexception"],"backgroundTag":null,"analyzedSha":"c236dd0f930f831ec151e436e138ddc429a02a72","analyzedAt":"2026-08-13T20:27:11.027Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}