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
- 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.
- 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.
- Validate the lock TTL is a parseable duration and large enough that renewals are not starved by retries.
- If the error stems from context cancellation, cancel the parent context yourself so the shutdown path is clean instead of letting Renew fail unexpectedly.
- 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
- Ensure network reliability to the API server; renewal heartbeats run every 0.7*TTL
- Set a TTL that absorbs transient blips and the client's 5-attempt retry window
- Monitor the server's health; restarts/outages surface as renewal failures
- Filter context cancellation in your own code so shutdowns don't masquerade as renewal errors
- Re-acquire the lock and resume work after transient renewal failures rather than crashing
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
- release conflict %w
- renew conflict %w
- lock release: %w
- ack.Error
- failed to derive Consul token for task %s: %v
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/17acaf99f0028033.
Report an issue: GitHub.