nats-io/nats-server · error

unable to get auth session: %v

Error message

unable to get auth session: %v

What it means

unsealJsEncrpytionKey could not create the policy PCR+password authorization session via policyPCRPasswordSession. The wrapped error from StartAuthSession (or later policy commands) is embedded in %v.

Source

Thrown at server/tpm/js_ek_tpm_windows.go:189

		return "", fmt.Errorf("unable to write key file: %v", err)
	}
	return string(jsStoreKey), nil
}

// Unseals the JetStream encryption key from the TPM with the provided keys.
// The key is returned as a string.
func unsealJsEncrpytionKey(rwc io.ReadWriteCloser, pcr int, srkHandle tpmutil.Handle, srkPassword, objectPassword string, publicBlob, privateBlob []byte) (string, error) {
	// Load the public/private blobs into the TPM for decryption.
	objectHandle, _, err := tpm2.Load(rwc, srkHandle, srkPassword, publicBlob, privateBlob)
	if err != nil {
		return "", fmt.Errorf("unable to load data: %v", err)
	}
	defer tpm2.FlushContext(rwc, objectHandle)

	// Create the authorization session with TPM.
	sessHandle, _, err := policyPCRPasswordSession(rwc, pcr)
	if err != nil {
		return "", fmt.Errorf("unable to get auth session: %v", err)
	}
	defer func() {
		tpm2.FlushContext(rwc, sessHandle)
	}()
	// Unseal the data we've loaded into the TPM with the object (js key) password.
	unsealedData, err := tpm2.UnsealWithSession(rwc, sessHandle, objectHandle, objectPassword)
	if err != nil {
		return "", fmt.Errorf("unable to unseal data: %v", err)
	}
	return string(unsealedData), nil
}

// Returns session handle and policy digest.
func policyPCRPasswordSession(rwc io.ReadWriteCloser, pcr int) (sessHandle tpmutil.Handle, policy []byte, retErr error) {
	sessHandle, _, err := tpm2.StartAuthSession(
		rwc,
		tpm2.HandleNull,  /*tpmKey*/
		tpm2.HandleNull,  /*bindKey*/

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check the embedded %v cause from StartAuthSession.
  2. Validate the configured pcr index is a valid SHA-256 PCR (0-23 on typical TPMs).
  3. Restart the process/host to clear leaked sessions if the session table is full.
  4. Confirm the TPM device is healthy (run tpm2_getcap or vendor diagnostics).

Example fix

// before: unchecked PCR value
key, err := unsealJsEncrpytionKey(rwc, pcr, srkHandle, ...)
// after: validate pcr config first
if pcr < 0 || pcr > 23 {
	return "", fmt.Errorf("invalid PCR index %d", pcr)
}
key, err := unsealJsEncrpytionKey(rwc, pcr, srkHandle, ...)
Defensive patterns

Strategy: retry

Validate before calling

if pcr < 0 || pcr > 23 {
	return fmt.Errorf("invalid PCR index %d", pcr)
}
if _, err := tpm2.ReadPCRs(rwc, tpm2.PCRSelection{Hash: tpm2.AlgSHA256, PCRs: []int{pcr}}); err != nil {
	return fmt.Errorf("PCR %d not readable: %w", pcr, err)
}

Try / catch

var key string
var err error
for i := 0; i < 3; i++ {
	key, err = LoadJetStreamEncryptionKeyFromTPM(rwc)
	if err == nil || !strings.Contains(err.Error(), "unable to get auth session") {
		break
	}
	time.Sleep(200 * time.Millisecond)
}

Prevention

When it happens

Trigger: policyPCRPasswordSession(rwc, pcr) returns error — TPM auth session limit reached, invalid PCR index, or TPM communication failure.

Common situations: Invalid pcr value configured (out of range for the PCR bank); TPM session table full from leaked sessions; TPM device in a failed/locked state.

Related errors


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/97ef654110572481. Report an issue: GitHub.