nats-io/nats-server · error
unable to write key file: %v
Error message
unable to write key file: %v
What it means
After a successful tpm2.Seal, the returned private and public blobs could not be persisted to the JetStream key file on disk. The wrapping writeTPMKeysToFile error (permissions, path, disk space) is embedded in %v.
Source
Thrown at server/tpm/js_ek_tpm_windows.go:171
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)
// Create the authorization session with TPM.
sessHandle, _, err := policyPCRPasswordSession(rwc, pcr)
if err != nil {
return "", fmt.Errorf("unable to get auth session: %v", err)View on GitHub (pinned to 3a66a489d2)
Solutions
- Read the embedded %v cause to identify the OS-level file error.
- Verify the directory containing jsKeyFile exists and the process user has write permission.
- Create the parent directory before calling LoadJetStreamEncryptionKeyFromTPM.
- Check disk space and that the path isn't on a read-only mount.
Example fix
// before: directory may not exist
err = writeTPMKeysToFile(jsKeyFile, privateArea, publicArea)
// after: ensure directory exists first
if err := os.MkdirAll(filepath.Dir(jsKeyFile), 0o700); err != nil {
return "", fmt.Errorf("unable to create key dir: %v", err)
}
err = writeTPMKeysToFile(jsKeyFile, privateArea, publicArea) Defensive patterns
Strategy: validation
Validate before calling
dir := filepath.Dir(jsKeyFile)
if info, err := os.Stat(dir); err != nil || !info.IsDir() {
return fmt.Errorf("key file directory %q missing", dir)
}
test, err := os.CreateTemp(dir, ".writecheck")
if err != nil {
return fmt.Errorf("directory %q not writable: %w", dir, err)
}
test.Close()
os.Remove(test.Name()) Try / catch
if err != nil {
if os.IsPermission(err) {
log.Printf("permission denied writing key file %s: %v", jsKeyFile, err)
}
return err
} Prevention
- Create the key file directory with MkdirAll(0o700) at startup.
- Run the service under an account with write access to the key directory.
- Check free disk space before writing.
- Keep key file paths out of read-only mounts/containers.
When it happens
Trigger: writeTPMKeysToFile(jsKeyFile, privateArea, publicArea) fails — bad jsKeyFile path, unwritable directory, disk full, or the parent directory doesn't exist.
Common situations: Running under a service account lacking write permission to the configured key file directory; jsKeyFile path misconfigured; read-only filesystem or container volume.
Understand the failure class
Background: "Permission denied" / "Failed to write" file errors: why a library can't write its files to disk (EACCES, EPERM, ENOSPC) and how to fix them — this error's family across 43 libraries.
Related errors
- unable to seal data: %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/665d891a14511083.
Report an issue: GitHub.