juanfont/headscale · error

failed to generate API key

Error message

failed to generate API key

What it means

Sentinel in hscontrol/db/api_key.go returned by CreateAPIKey when generating the key material itself fails. CreateAPIKey builds a 12-char hex prefix and 64-char hex secret via tailscale.com/util/rands and hashes the secret; failure at that stage (essentially only a broken crypto/rand source) aborts creation before the row is persisted.

Source

Thrown at hscontrol/db/api_key.go:27

	"github.com/juanfont/headscale/hscontrol/types"
	"golang.org/x/crypto/bcrypt"
	"gorm.io/gorm"
	"tailscale.com/util/rands"
)

const (
	apiKeyPrefix       = "hskey-api-" //nolint:gosec // This is a prefix, not a credential
	apiKeyPrefixLength = 12
	apiKeyHashLength   = 64

	// Legacy format constants.
	legacyAPIPrefixLength = 7
	legacyAPIKeyLength    = 32
)

var (
	ErrAPIKeyFailedToParse    = errors.New("failed to parse ApiKey")
	ErrAPIKeyGenerationFailed = errors.New("failed to generate API key")
	ErrAPIKeyExpired          = errors.New("API key expired")
)

// CreateAPIKey creates a new [types.APIKey] in a user, and returns it.
func (hsdb *HSDatabase) CreateAPIKey(
	expiration *time.Time,
) (string, *types.APIKey, error) {
	// Generate public prefix (12 chars)
	prefix := rands.HexString(apiKeyPrefixLength)

	// Generate secret (64 chars)
	secret := rands.HexString(apiKeyHashLength)

	// Full key string (shown ONCE to user)
	keyStr := apiKeyPrefix + prefix + "-" + secret

	// bcrypt hash of secret
	hash, err := bcrypt.GenerateFromPassword([]byte(secret), bcrypt.DefaultCost)

View on GitHub (pinned to 565fd254d0)

Solutions

  1. Verify /dev/urandom exists and is readable inside the container/host running headscale
  2. Retry key creation once — transient entropy starvation is possible early after boot
  3. If running in a restricted sandbox, expose a working entropy source to the process
Defensive patterns

Strategy: retry

Try / catch

key, ak, err := hsdb.CreateAPIKey(&exp)
if err != nil {
    if errors.Is(err, db.ErrAPIKeyGenerationFailed) {
        // entropy-source failure: safe to retry once; otherwise surface it
        key, ak, err = hsdb.CreateAPIKey(&exp)
    }
    if err != nil {
        return fmt.Errorf("creating API key: %w", err)
    }
}

Prevention

When it happens

Trigger: Calling HSDatabase.CreateAPIKey(expiration) on a host where the system entropy source is unavailable or crypto/rand errors, causing prefix/secret generation to fail. Extremely rare in practice.

Common situations: Stripped-down containers with no /dev/urandom; exotic runtimes (some wasm/sandbox environments) where crypto/rand is not backed by a working entropy device.

Related errors


AI-assisted analysis of juanfont/headscale@565fd254d0 (2026-08-15). Data as JSON: /api/errors/503f0920fe767920. Report an issue: GitHub.