nats-io/nats-server · critical
unable to seal data: %v
Error message
unable to seal data: %v
What it means
createAndSealJsEncryptionKey failed to seal the newly generated JetStream encryption key into the TPM. tpm2.Seal wraps jsStoreKey under the SRK with a policy-PCR+password session; any TPM-level failure (handle, auth, policy mismatch) surfaces here wrapped with the underlying cause in %v.
Source
Thrown at server/tpm/js_ek_tpm_windows.go:167
if err != nil {
return "", fmt.Errorf("unable to get policy: %v", err)
}
if err := tpm2.FlushContext(rwc, sessHandle); err != nil {
return "", fmt.Errorf("unable to flush session: %v", err)
}
// Seal the data to the parent key and the policy
user, err := nkeys.CreateUser()
if err != nil {
return "", fmt.Errorf("unable to create seed: %v", err)
}
// We'll use the seed to represent the encryption key.
jsStoreKey, err := user.Seed()
if err != nil {
return "", fmt.Errorf("unable to get seed: %v", err)
}
privateArea, publicArea, err := tpm2.Seal(rwc, srkHandle, srkPassword, jsKeyPassword, policy, jsStoreKey)
if err != nil {
return "", fmt.Errorf("unable to seal data: %v", err)
}
err = writeTPMKeysToFile(jsKeyFile, privateArea, publicArea)
if err != nil {
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)
View on GitHub (pinned to 3a66a489d2)
Solutions
- Inspect the wrapped %v cause for the actual TPM error (auth failure vs I/O).
- Verify the SRK handle (e.g. tpm2.HandleOwner) exists and srkPassword is correct (often empty string).
- Confirm the TPM device is open and responsive before calling (see LoadJetStreamEncryptionKeyFromTPM).
- Ensure the policy session (policyPCRPasswordSession) succeeds and PCRs haven't changed since sealing.
Example fix
// before: guessing srk password privateArea, publicArea, err := tpm2.Seal(rwc, srkHandle, srkPassword, jsKeyPassword, policy, jsStoreKey) // after: ensure SRK exists with empty auth before sealing srkHandle := tpm2.HandleOwner srkPassword := "" // default SRK auth unless changed privateArea, publicArea, err := tpm2.Seal(rwc, srkHandle, srkPassword, jsKeyPassword, policy, jsStoreKey)
Defensive patterns
Strategy: try-catch
Validate before calling
f, err := os.OpenFile(tpmDevicePath, os.O_RDWR, 0)
if err != nil {
return fmt.Errorf("TPM device unavailable: %w", err)
}
f.Close() Type guard
func isTPMSealError(err error) bool {
return err != nil && strings.Contains(err.Error(), "unable to seal data")
} Try / catch
key, err := LoadJetStreamEncryptionKeyFromTPM(rwc)
if err != nil {
if strings.Contains(err.Error(), "unable to seal data") {
log.Printf("TPM seal failed: %v — verify SRK handle and PCR state", err)
}
return err
} Prevention
- Verify the SRK persistent handle exists before sealing.
- Keep srkPassword consistent (usually empty) across seal and unseal.
- Test TPM connectivity at startup with a trivial TPM2_GetCapability call.
- Log the wrapped cause verbatim for diagnosis.
When it happens
Trigger: tpm2.Seal(rwc, srkHandle, srkPassword, jsKeyPassword, policy, jsStoreKey) returns non-nil err — e.g. bad SRK handle/password, SRK not created, policy digest mismatch, or TPM device I/O error.
Common situations: Wrong srkHandle or srkPassword passed in; TPM not accessible or not initialized; PCR state changed so the policy session is invalid; first run where the SRK persistent handle doesn't exist.
Related errors
- unable to write key file: %v
- unable to load data: %v
- unable to get auth session: %v
- unable to unseal data: %v
- unable to start session: %v
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/4ac63bee5252c2ad.
Report an issue: GitHub.