hashicorp/nomad · error

error executing protected function %w

Error message

error executing protected function %w

What it means

This error is sent through the leaser's error channel when one of the protected functions passed to LockLeaser.Start returns an error. The library aborts the remaining protected functions, cancels the function context, and Start surfaces the wrapped error. It is not a lock/HTTP error — it means your own callback failed while holding the lock.

Source

Thrown at api/locks.go:321

			if !errors.Is(err, ErrLockConflict) {
				errChannel <- err
			}
		}

		if lockID != "" {
			ll.locked = true

			funcCtx, funcCancel := context.WithCancel(ctx)
			defer funcCancel()

			// 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():

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Fix the underlying error inside the protected function — inspect the wrapped cause with errors.Is/As on the error returned by Start (it is prefixed 'error executing protected function').
  2. If the error is benign (e.g. context canceled during shutdown), swallow or filter it inside the protected function and return nil instead.
  3. Log and return an error only for real failures; treat context.Canceled/DeadlineExceeded separately from operational errors.
  4. If remaining protected functions must always run, do not return early on failure — collect and log per-function errors.
  5. Retry the Start call with backoff if the failing protected work is transient (e.g. network to a backing service).

Example fix

// before
func(ctx context.Context) error {
    return db.Ping(ctx) // aborts all protected funcs on failure
}
// after
func(ctx context.Context) error {
    if err := db.Ping(ctx); err != nil {
        if ctx.Err() != nil {
            return nil // shutting down; don't abort the leaser
        }
        return err
    }
    return nil
}
Defensive patterns

Strategy: try-catch

Validate before calling

// validate dependencies the protected function needs BEFORE starting the leaser
if err := db.Ping(context.Background()); err != nil {
    return fmt.Errorf("precheck failed, not starting leaser: %w", err)
}

Type guard

func IsProtectedFuncError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "error executing protected function")
}

Try / catch

err := leaser.Start(ctx, work)
if err != nil {
    if errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {
        // shutdown, not a real failure
        return nil
    }
    return fmt.Errorf("protected work failed: %w", err)
}

Prevention

When it happens

Trigger: Any protectedFunc passed to LockLeaser.Start(ctx, f1, f2, ...) returning a non-nil error; f1 succeeds but a later fN fails, cancelling the funcCtx mid-run; the protected function returns an error because its own context was cancelled by an outer shutdown that it treats as fatal.

Common situations: Application logic inside the critical section hitting a database/connection error; a protected function returning ctx.Err() (context.Canceled) after the parent context is cancelled during shutdown; misconfigured dependencies used inside the protected function; the function attempting work that is no longer valid once the lease is lost.

Related errors


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