HangfireIO/Hangfire · error · SqlServerDistributedLockException

Could not place a lock on the resource '{resource}': {messag

Error message

Could not place a lock on the resource '{resource}': {message}.

What it means

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.

Source

Thrown at src/Hangfire.SqlServer/SqlServerDistributedLock.cs:215

                    .AddParameter("@DbPrincipal", "public", DbType.String, size: 32)
                    .AddParameter("@LockMode", LockMode, DbType.String, size: 32)
                    .AddParameter("@LockOwner", LockOwner, DbType.String, size: 32)
                    .AddParameter("@LockTimeout", lockTimeout, DbType.Int32)
                    .AddReturnParameter("@Result", out var resultParameter, DbType.Int32);

                command.ExecuteNonQuery();

                var lockResult = (int)resultParameter.Value;

                if (lockResult >= 0)
                {
                    // The lock has been successfully obtained on the specified resource.
                    return;
                }

                if (lockResult == -999 /* Indicates a parameter validation or other call error. */)
                {
                    throw new SqlServerDistributedLockException(
                        $"Could not place a lock on the resource '{resource}': {(LockErrorMessages.TryGetValue(lockResult, out var message) ? message : $"Server returned the '{lockResult}' error.")}.");
                }
            } while (started.Elapsed < timeout);

            throw new DistributedLockTimeoutException(resource);
        }

        internal static void Release(DbConnection connection, string resource)
        {
            using (var command = CreateReleaseCommand(connection, resource, out var resultParameter))
            {
                command.ExecuteNonQuery();

                var releaseResult = (int)resultParameter.Value;

                if (releaseResult < 0)
                {
                    throw new SqlServerDistributedLockException(

View on GitHub (pinned to c236dd0f93)

Solutions

  1. Grant the connecting database user membership in the db_owner role (or at minimum EXECUTE on sp_getapplock) and retry.
  2. Verify the connection string's credentials map to a principal with permissions in the target database.
  3. Check SQL Server error logs for the detailed sp_getapplock failure around the time of the exception.
  4. Ensure the Hangfire schema is installed in the correct database and the resource name does not exceed 255 characters.

Example fix

// before
var storage = new SqlServerStorage("Server=.;Database=Hangfire;User Id=appuser;Password=...;");
// after — grant db_owner or EXECUTE on sp_getapplock to appuser, e.g.:
//   ALTER ROLE db_owner ADD MEMBER appuser;
var storage = new SqlServerStorage("Server=.;Database=Hangfire;User Id=appuser;Password=...;");
Defensive patterns

Strategy: try-catch

Validate before calling

// No purely client-side check prevents a DB-side parameter-validation error.
// Pre-validate that the database user has db_owner or EXECUTE on sp_getapplock:
using var cmd = connection.CreateCommand();
cmd.CommandText = "SELECT HAS_PERMS_BY_NAME(DB_NAME(), 'DATABASE', 'EXECUTE')";
var hasPermission = (int)cmd.ExecuteScalar() == 1;

Try / catch

try
{
    using (storage.GetConnection().AcquireDistributedLock(resource, timeout))
    {
        DoWork();
    }
}
catch (SqlServerDistributedLockException ex) when (ex.Message.Contains("-999"))
{
    // Hard parameter-validation error: check DB permissions and schema.
    logger.Error(ex, "Distributed lock acquisition failed with a call error for {Resource}", resource);
    throw;
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of HangfireIO/Hangfire@c236dd0f93 (2026-08-13). Data as JSON: /api/errors/931fd9b450a22c03. Report an issue: GitHub.