nats-io/nats-server · error

unable to get policy digest: %v

Error message

unable to get policy digest: %v

What it means

policyPCRPasswordSession could not retrieve the final policy digest via tpm2.PolicyGetDigest after binding PCR and password checks. This digest is required for the subsequent Seal operation, so the whole session is abandoned.

Source

Thrown at server/tpm/js_ek_tpm_windows.go:236

			if err := tpm2.FlushContext(rwc, sessHandle); err != nil {
				retErr = fmt.Errorf("%v\nunable to flush session: %v", retErr, err)
			}
		}
	}()

	pcrSelection := tpm2.PCRSelection{
		Hash: tpm2.AlgSHA256,
		PCRs: []int{pcr},
	}
	if err := tpm2.PolicyPCR(rwc, sessHandle, nil, pcrSelection); err != nil {
		return sessHandle, nil, fmt.Errorf("unable to bind PCRs to auth policy: %v", err)
	}
	if err := tpm2.PolicyPassword(rwc, sessHandle); err != nil {
		return sessHandle, nil, fmt.Errorf("unable to require password for auth policy: %v", err)
	}
	policy, err = tpm2.PolicyGetDigest(rwc, sessHandle)
	if err != nil {
		return sessHandle, nil, fmt.Errorf("unable to get policy digest: %v", err)
	}
	return sessHandle, policy, nil
}

// LoadJetStreamEncryptionKeyFromTPM loads the JetStream encryption key from the TPM.
// If the keyfile does not exist, a key will be created and sealed. Public and private blobs
// used to decrypt the key in future sessions will be saved to disk in the file provided.
// The key will be unsealed and returned only with the correct password and PCR value.
func LoadJetStreamEncryptionKeyFromTPM(srkPassword, jsKeyFile, jsKeyPassword string, pcr int) (string, error) {
	rwc, err := tpm2.OpenTPM()
	if err != nil {
		return "", fmt.Errorf("could not open the TPM: %v", err)
	}
	defer rwc.Close()

	// Load the key from the TPM
	srkHandle, err := regenerateSRK(rwc, srkPassword)
	defer func() {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Check the embedded %v cause for the TPM return code.
  2. Ensure no other process/thread flushes the same session concurrently.
  3. Retry the full policyPCRPasswordSession call.
  4. Verify TPM device connectivity (rwc) is stable.

Example fix

// before
tp2policy, err = tpm2.PolicyGetDigest(rwc, sessHandle)
// after: retry session as a unit on digest failure
policy, err = tpm2.PolicyGetDigest(rwc, sessHandle)
if err != nil {
	tpm2.FlushContext(rwc, sessHandle)
	return tpm2.HandleNull, nil, fmt.Errorf("unable to get policy digest: %v", err)
}
Defensive patterns

Strategy: retry

Type guard

func isPolicyDigestError(err error) bool {
	return err != nil && strings.Contains(err.Error(), "unable to get policy digest")
}

Try / catch

sessHandle, policy, err := policyPCRPasswordSession(rwc, pcr)
if err != nil && isPolicyDigestError(err) {
	time.Sleep(200 * time.Millisecond)
	sessHandle, policy, err = policyPCRPasswordSession(rwc, pcr) // full retry
}

Prevention

When it happens

Trigger: tpm2.PolicyGetDigest(rwc, sessHandle) errors — invalid session handle or TPM I/O failure after the earlier policy commands succeeded.

Common situations: Connection dropped between policy commands and digest retrieval; session flushed by a concurrent TPM user; TPM device error.

Related errors


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