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
- Check the embedded %v cause for the TPM return code.
- Ensure no other process/thread flushes the same session concurrently.
- Retry the full policyPCRPasswordSession call.
- 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
- Always retry the whole policyPCRPasswordSession, not just PolicyGetDigest.
- Ensure the rwc connection is stable (no mid-operation close).
- Serialize TPM access to prevent other code from flushing your session.
- Flush the session handle explicitly when abandoning a partial session.
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
- unable to bind PCRs to auth policy: %v
- unable to require password for auth policy: %v
- unable to seal data: %v
- unable to write key file: %v
- unable to load data: %v
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/562a8a327e24114b.
Report an issue: GitHub.