{"record":{"id":"fa88b191b134d9bf","repo":"nats-io/nats-server","slug":"raft-storage-directory-is-not-writable","errorCode":null,"errorMessage":"raft: storage directory is not writable","messagePattern":"raft: storage directory is not writable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"server/raft.go","lineNumber":441,"sourceCode":"\t\t}\n\n\t\tif expected < nrs+ngwps {\n\t\t\texpected = nrs + ngwps\n\t\t\ts.Debugf(\"Adjusting expected peer set size to %d with %d known\", expected, len(knownPeers))\n\t\t}\n\t}\n\n\t// Check the store directory. If we have a memory based WAL we need to make sure the directory is setup.\n\tif stat, err := os.Stat(cfg.Store); os.IsNotExist(err) {\n\t\tif err := os.MkdirAll(cfg.Store, defaultDirPerms); err != nil {\n\t\t\treturn fmt.Errorf(\"raft: could not create storage directory - %v\", err)\n\t\t}\n\t} else if stat == nil || !stat.IsDir() {\n\t\treturn fmt.Errorf(\"raft: storage directory is not a directory\")\n\t}\n\ttmpfile, err := os.CreateTemp(cfg.Store, \"_test_\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"raft: storage directory is not writable\")\n\t}\n\ttmpfile.Close()\n\tos.Remove(tmpfile.Name())\n\n\treturn writePeerState(s.diskIOSemaphore(), cfg.Store, &peerState{knownPeers, expected, extUndetermined})\n}\n\n// initRaftNode will initialize the raft node, to be used by startRaftNode or when testing to not run the Go routine.\nfunc (s *Server) initRaftNode(accName string, cfg *RaftConfig, labels pprofLabels) (*raft, error) {\n\trestorePeerState := func(n *raft) error {\n\t\tps, err := readPeerState(s.diskIOSemaphore(), cfg.Store)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif ps == nil {\n\t\t\treturn errNoPeerState\n\t\t}\n\t\tn.processPeerState(ps)","sourceCodeStart":423,"sourceCodeEnd":459,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/raft.go#L423-L459","documentation":"After confirming cfg.Store is a directory, the Raft setup performs a write probe with os.CreateTemp in that directory. If creating the temp file fails (permissions, read-only filesystem, disk full), setup returns this error, indicating the storage directory is unusable for the WAL.","triggerScenarios":"os.CreateTemp(cfg.Store, \"_test_\") fails: directory has no write permission for the server user, filesystem is mounted read-only, or the underlying device has no space/inodes.","commonSituations":"Running NATS as a non-root user against a root-owned JetStream store dir, Docker/K8s volume mounted readOnly, or full disk after WAL growth.","solutions":["chown/chmod the storage directory so the server process has write access (e.g. chown -R nats:nats /data/js).","Remount the volume read-write or move store_dir to a writable path.","Free disk space / inodes on the volume, then restart the server.","Check server logs for the underlying os error to identify ENOSPC vs EACCES vs EROFS."],"exampleFix":"// before\n# ls -ld /data/js\ndr-xr-xr-x root root /data/js\n// after\nchown -R nats:nats /data/js && chmod u+rwx /data/js","handlingStrategy":"validation","validationCode":"if err := os.MkdirAll(storeDir, 0750); err != nil { return err }\nprobe, err := os.CreateTemp(storeDir, \"_write_probe_\")\nif err != nil { return err }\nprobe.Close(); os.Remove(probe.Name())","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Ensure the server's OS user owns or can write the JetStream store directory.","Do not mount store volumes read-only.","Monitor disk space/inodes on store volumes."],"tags":["raft","filesystem","permissions"],"backgroundTag":"permission-denied","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}