{"record":{"id":"931fd9b450a22c03","repo":"HangfireIO/Hangfire","slug":"could-not-place-a-lock-on-the-resource-resource","errorCode":null,"errorMessage":"Could not place a lock on the resource '{resource}': {message}.","messagePattern":"Could not place a lock on the resource '(.+?)': (.+?)\\.","errorType":"exception","errorClass":"SqlServerDistributedLockException","httpStatus":null,"severity":"error","filePath":"src/Hangfire.SqlServer/SqlServerDistributedLock.cs","lineNumber":215,"sourceCode":"                    .AddParameter(\"@DbPrincipal\", \"public\", DbType.String, size: 32)\n                    .AddParameter(\"@LockMode\", LockMode, DbType.String, size: 32)\n                    .AddParameter(\"@LockOwner\", LockOwner, DbType.String, size: 32)\n                    .AddParameter(\"@LockTimeout\", lockTimeout, DbType.Int32)\n                    .AddReturnParameter(\"@Result\", out var resultParameter, DbType.Int32);\n\n                command.ExecuteNonQuery();\n\n                var lockResult = (int)resultParameter.Value;\n\n                if (lockResult >= 0)\n                {\n                    // The lock has been successfully obtained on the specified resource.\n                    return;\n                }\n\n                if (lockResult == -999 /* Indicates a parameter validation or other call error. */)\n                {\n                    throw new SqlServerDistributedLockException(\n                        $\"Could not place a lock on the resource '{resource}': {(LockErrorMessages.TryGetValue(lockResult, out var message) ? message : $\"Server returned the '{lockResult}' error.\")}.\");\n                }\n            } while (started.Elapsed < timeout);\n\n            throw new DistributedLockTimeoutException(resource);\n        }\n\n        internal static void Release(DbConnection connection, string resource)\n        {\n            using (var command = CreateReleaseCommand(connection, resource, out var resultParameter))\n            {\n                command.ExecuteNonQuery();\n\n                var releaseResult = (int)resultParameter.Value;\n\n                if (releaseResult < 0)\n                {\n                    throw new SqlServerDistributedLockException(","sourceCodeStart":197,"sourceCodeEnd":233,"githubUrl":"https://github.com/HangfireIO/Hangfire/blob/c236dd0f930f831ec151e436e138ddc429a02a72/src/Hangfire.SqlServer/SqlServerDistributedLock.cs#L197-L233","documentation":"SQL Server's sp_getapplock stored procedure returned error code -999 when Hangfire tried to acquire a distributed lock, indicating a parameter-validation or call-level error. This is a hard, non-retryable failure — the lock request itself is malformed or the database principal lacks authorization. The exception type is SqlServerDistributedLockException.","triggerScenarios":"Any Hangfire server operation that acquires a distributed lock (ExpirationManager, CountersAggregator, recurring/delayed job scheduling) when the @DbPrincipal parameter 'public' is not authorized, the @LockMode/@LockOwner values are rejected by the SQL Server version, or the connection's database principal lacks the required permissions.","commonSituations":"Restricted database user accounts that lack EXECUTE permission on sp_getapplock; connecting to a database where the calling principal is not mapped correctly; SQL Server edition/version differences in applock behavior; custom lock resource names that exceed the 255-character parameter size.","solutions":["Grant the connecting database user membership in the db_owner role (or at minimum EXECUTE on sp_getapplock) and retry.","Verify the connection string's credentials map to a principal with permissions in the target database.","Check SQL Server error logs for the detailed sp_getapplock failure around the time of the exception.","Ensure the Hangfire schema is installed in the correct database and the resource name does not exceed 255 characters."],"exampleFix":"// before\nvar storage = new SqlServerStorage(\"Server=.;Database=Hangfire;User Id=appuser;Password=...;\");\n// after — grant db_owner or EXECUTE on sp_getapplock to appuser, e.g.:\n//   ALTER ROLE db_owner ADD MEMBER appuser;\nvar storage = new SqlServerStorage(\"Server=.;Database=Hangfire;User Id=appuser;Password=...;\");","handlingStrategy":"try-catch","validationCode":"// No purely client-side check prevents a DB-side parameter-validation error.\n// Pre-validate that the database user has db_owner or EXECUTE on sp_getapplock:\nusing var cmd = connection.CreateCommand();\ncmd.CommandText = \"SELECT HAS_PERMS_BY_NAME(DB_NAME(), 'DATABASE', 'EXECUTE')\";\nvar hasPermission = (int)cmd.ExecuteScalar() == 1;","typeGuard":null,"tryCatchPattern":"try\n{\n    using (storage.GetConnection().AcquireDistributedLock(resource, timeout))\n    {\n        DoWork();\n    }\n}\ncatch (SqlServerDistributedLockException ex) when (ex.Message.Contains(\"-999\"))\n{\n    // Hard parameter-validation error: check DB permissions and schema.\n    logger.Error(ex, \"Distributed lock acquisition failed with a call error for {Resource}\", resource);\n    throw;\n}","preventionTips":["Grant the Hangfire database user db_owner role or EXECUTE permission on sp_getapplock.","Use Hangfire's built-in lock APIs (AcquireDistributedLock) rather than calling sp_getapplock directly.","Run the Hangfire schema installer (PrepareSchemaIfNecessary) to ensure required objects exist."],"tags":["distributed-lock","sql-server","permissions","sp-getapplock"],"backgroundTag":null,"analyzedSha":"c236dd0f930f831ec151e436e138ddc429a02a72","analyzedAt":"2026-08-13T20:27:11.027Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}