hashicorp/nomad · error

error renewing the lease: %w

Error message

error renewing the lease: %w

What it means

This error is produced by LockLeaser's internal start when maintainLease fails to renew the lock with an error other than ErrLockConflict. It means the lease-maintenance heartbeat (Renew) failed for a non-conflict reason — e.g. network or API errors — so the library cannot guarantee the lock is still held. ErrLockConflict renewals are deliberately filtered out and not reported through this message.

Source

Thrown at api/locks.go:332

			// Execute the lock protected function.
			go func() {
				defer funcCancel()
				for _, f := range protectedFuncs {
					err := f(funcCtx)
					if err != nil {
						errChannel <- fmt.Errorf("error executing protected function %w", err)
						return
					}
					cancel()
				}
			}()

			// Maintain lease is a blocking function, it will return if there is
			// an error maintaining the lease or the protected function returned.
			err = ll.maintainLease(funcCtx)
			if err != nil && !errors.Is(err, ErrLockConflict) {
				errChannel <- fmt.Errorf("error renewing the lease: %w", err)
			}
		}

		waitTicker.Stop()
		waitTicker = time.NewTicker(ll.waitPeriod)
		select {
		case <-ctx.Done():
			return nil

		case err := <-errChannel:
			return fmt.Errorf("locks: %w", err)

		case <-waitTicker.C:
		}
	}
}

func (ll *LockLeaser) maintainLease(ctx context.Context) error {

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Inspect the wrapped cause with errors.Is/As on the error from Start (prefix 'error renewing the lease:') — any surfaced cause is a transport/API error, since ErrLockConflict is filtered out earlier.
  2. Check connectivity to the server and retry; the internal exponential retry already retries until 5 attempts or TTL expiry, so persistent causes need infrastructure fixes.
  3. Validate the lock TTL is a parseable duration and large enough that renewals are not starved by retries.
  4. If the error stems from context cancellation, cancel the parent context yourself so the shutdown path is clean instead of letting Renew fail unexpectedly.
  5. For lost-lock semantics, rely on errors.Is(err, api.ErrLockConflict) handling; this specific error indicates non-conflict failures.

Example fix

// before
if err := leaser.Start(ctx, work); err != nil {
    log.Fatal(err)
}
// after
if err := leaser.Start(ctx, work); err != nil {
    var netErr net.Error
    if errors.As(err, &netErr) {
        log.Println("transient lease renewal failure, restarting leaser")
        restartLeaserWithBackoff()
    } else {
        log.Fatal(err)
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// preflight: validate TTL config and server reachability before starting the leaser
if _, err := time.ParseDuration(v.Lock.TTL); err != nil {
    return fmt.Errorf("invalid lock TTL %q: %w", v.Lock.TTL, err)
}
if err := pingServer(ctx); err != nil {
    return fmt.Errorf("server unreachable before starting leaser: %w", err)
}

Type guard

func IsLeaseRenewalFailure(err error) bool {
    return err != nil && strings.Contains(err.Error(), "error renewing the lease")
}

Try / catch

err := leaser.Start(ctx, work)
if err != nil {
    if strings.Contains(err.Error(), "error renewing the lease") {
        // transient transport/API failure: restart the leaser with backoff
        return restartWithBackoff(ctx)
    }
    return err
}

Prevention

When it happens

Trigger: maintainLease's periodic locker.Renew call returns a non-ErrLockConflict error: server 500s, connection refused/timeouts, invalid TTL configuration causing retryPut to exhaust its retries, or the client losing connectivity mid-lease.

Common situations: Consul/API server outage or restart while the leaser holds a lock; network partition between the client and server; misconfigured Variable.Lock.TTL string (e.g. unparseable duration) leading to failed calls; load balancer dropping long-lived connections during renewal; retry limits (defaultNumberOfRetries) exhausted under transient errors.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/17acaf99f0028033. Report an issue: GitHub.