Tencent/WeKnora · critical

failed to generate JWT secret: %v

Error message

failed to generate JWT secret: %v

What it means

This panic occurs during lazy JWT secret initialization in the user service. After checking the JWT_SECRET env var, the code falls back to crypto/rand to generate 32 random bytes; if the OS entropy source fails, it panics with the wrapped error. It is an intentionally fatal startup failure because a JWT secret is mandatory for signing tokens.

Source

Thrown at internal/application/service/user.go:89

// Machine-readable change-password failure reasons for HTTP details fields.
const (
	DetailInvalidOldPassword = "invalid_old_password"
	DetailPasswordPolicy     = "password_policy"
	DetailSamePassword       = "same_password"
)

// getJwtSecret retrieves the JWT secret from the environment, falling back to a securely generated random secret.
func getJwtSecret() string {
	jwtSecretOnce.Do(func() {
		if envSecret := strings.TrimSpace(os.Getenv("JWT_SECRET")); envSecret != "" {
			jwtSecret = envSecret
			return
		}

		randomBytes := make([]byte, 32)
		if _, err := rand.Read(randomBytes); err != nil {
			panic(fmt.Sprintf("failed to generate JWT secret: %v", err))
		}
		jwtSecret = base64.StdEncoding.EncodeToString(randomBytes)
	})

	return jwtSecret
}

// userService implements the UserService interface
type userService struct {
	userRepo         interfaces.UserRepository
	tokenRepo        interfaces.AuthTokenRepository
	tenantService    interfaces.TenantService
	memberService    interfaces.TenantMemberService
	config           *config.Config
	systemSettingSvc interfaces.SystemSettingService
}

// NewUserService creates a new user service instance

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Set the JWT_SECRET environment variable so the rand fallback is never taken
  2. Fix the runtime environment: update the container/seccomp profile to allow the getrandom syscall, or ensure /dev/urandom is available
  3. Retry process startup; rand.Read failure is usually transient on early boot
  4. Replace with a deterministic secret source (key file/KMS) if the platform cannot supply entropy

Example fix

// before
jwtSecret = envSecret
// after
export JWT_SECRET=$(openssl rand -base64 32)  # in deployment env, avoids rand fallback entirely
Defensive patterns

Strategy: fallback

Validate before calling

secret := os.Getenv("JWT_SECRET")
if secret == "" {
    if _, err := rand.Read(make([]byte, 32)); err != nil {
        return nil, fmt.Errorf("entropy unavailable: %w", err)
    }
}

Type guard

func hasJWTSecret() bool { return strings.TrimSpace(os.Getenv("JWT_SECRET")) != "" }

Try / catch

func jwtSecretSafe() (s string, err error) {
    defer func() { if r := recover(); r != nil { err = fmt.Errorf("jwt secret init panicked: %v", r) } }()
    return getJWTSecret(), nil
}

Prevention

When it happens

Trigger: First call to the JWT-secret singleton when JWT_SECRET is unset/empty and rand.Read on the 32-byte slice returns an error (e.g. getrandom(2) syscall failure, exhausted or blocked entropy source, restricted seccomp/sandbox blocking getrandom).

Common situations: Containers with constrained /dev/urandom or seccomp profiles that block the getrandom syscall; exotic OSes or minimal kernels lacking getrandom; early-boot environments where the random device is not yet ready.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/b62530e10142117d. Report an issue: GitHub.