hashicorp/nomad · warning

users: release of unused uid/gid

Error message

users: release of unused uid/gid

What it means

Sentinel ErrReleaseUnused from the dynamic users pool: Pool.Release was called with a uid/gid that is not currently in the pool's used set, meaning the caller tried to return an id that was never allocated or was already released.

Solutions

  1. Only release ids previously obtained from the pool's allocation
  2. Guard against double-release; check ownership before calling Release
  3. Treat as a bookkeeping bug in the caller

Example fix

// before
if err := pool.Release(ugid); err != nil { return err }
// after: track and clear
if ugid != 0 {
  if err := pool.Release(ugid); err != nil {
    if !errors.Is(err, dynamic.ErrReleaseUnused) { return err }
  }
  ugid = 0 // prevent double release
}
Defensive patterns

Strategy: try-catch

Validate before calling

// only release ids actually acquired and not yet released
if ugid == 0 || ugid < min || ugid > max || alreadyReleased[ugid] {
  return nil
}

Type guard

func releaseable(ugid UGID, min, max UGID, released map[UGID]bool) bool {
  return ugid >= min && ugid <= max && !released[ugid]
}

Try / catch

if err := p.Release(ugid); err != nil && !errors.Is(err, dynamic.ErrReleaseUnused) {
  return err // ignore benign double-release
}

Prevention

When it happens

Trigger: Calling Pool.Release(id) with an id that was never returned by Acquire, or calling Release twice with the same id.

Common situations: Double-release in client teardown paths; releasing a hardcoded/stale ugID after config changed the pool range; a client restarted with a different pool releasing IDs from the old run.

Related errors


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

Appendix: source

Thrown at helper/users/dynamic/pool.go:20

// SPDX-License-Identifier: BUSL-1.1

// Package dynamic provides a way of allocating UID/GID to be used by Nomad
// tasks with no associated service users managed by the operating system.
package dynamic

import (
	"errors"
	"math/rand"
	"strconv"
	"sync"

	"github.com/hashicorp/go-set/v3"
	"github.com/hashicorp/nomad/helper"
)

var (
	ErrPoolExhausted = errors.New("users: uid/gid pool exhausted")
	ErrReleaseUnused = errors.New("users: release of unused uid/gid")
	ErrCannotParse   = errors.New("users: unable to parse uid/gid from username")
)

// none indicates no dynamic user
const none = 0

// doNotEnable indicates functionality should be disabled
const doNotEnable = -1

// A UGID is a combination User (UID) and Group (GID). Since Nomad is
// allocating these values together from the same pool it can ensure they are
// always matching values, thus encoding them with one value.
type UGID int

// String returns the string representation of a UGID.
//
// It's just the numbers.
func (id UGID) String() string {

View on GitHub (pinned to 482b49bf1a)