nats-io/nats-server · critical
raft: could not create storage directory - %v
Error message
raft: could not create storage directory - %v
What it means
During raft group creation, if cfg.Store does not exist the server attempts os.MkdirAll with defaultDirPerms. Failure of that mkdir (permissions, full disk, bad path) is wrapped into this error; the underlying OS error is included via %v.
Source
Thrown at server/raft.go:434
// Each configured gateway URL represents one remote endpoint, so
// count it as a single peer. We must not resolve the host and add
// one per returned address: a hostname on a dual-stack host (e.g.
// "localhost" -> 127.0.0.1 + ::1) would then count the same server
// multiple times, inflating the expected meta-group size above the
// real node count and preventing meta leader election.
ngwps += len(gw.URLs)
}
if expected < nrs+ngwps {
expected = nrs + ngwps
s.Debugf("Adjusting expected peer set size to %d with %d known", expected, len(knownPeers))
}
}
// Check the store directory. If we have a memory based WAL we need to make sure the directory is setup.
if stat, err := os.Stat(cfg.Store); os.IsNotExist(err) {
if err := os.MkdirAll(cfg.Store, defaultDirPerms); err != nil {
return fmt.Errorf("raft: could not create storage directory - %v", err)
}
} else if stat == nil || !stat.IsDir() {
return fmt.Errorf("raft: storage directory is not a directory")
}
tmpfile, err := os.CreateTemp(cfg.Store, "_test_")
if err != nil {
return fmt.Errorf("raft: storage directory is not writable")
}
tmpfile.Close()
os.Remove(tmpfile.Name())
return writePeerState(s.diskIOSemaphore(), cfg.Store, &peerState{knownPeers, expected, extUndetermined})
}
// initRaftNode will initialize the raft node, to be used by startRaftNode or when testing to not run the Go routine.
func (s *Server) initRaftNode(accName string, cfg *RaftConfig, labels pprofLabels) (*raft, error) {
restorePeerState := func(n *raft) error {
ps, err := readPeerState(s.diskIOSemaphore(), cfg.Store)View on GitHub (pinned to 3a66a489d2)
Solutions
- Create/fix the store directory manually and grant write permission to the server's user: `mkdir -p <dir> && chown nats <dir>`.
- Check the underlying %v OS error for the precise cause (EACCES, EROFS, ENOSPC).
- Point cfg.Store (store_dir) at a writable volume with sufficient disk space.
- If the path exists as a regular file, remove or rename it so a directory can take its place.
Example fix
// before: store_dir under read-only mount store_dir: "/mnt/ro/jetstream" // after store_dir: "/var/lib/nats/jetstream" # writable by the nats user
Defensive patterns
Strategy: validation
Validate before calling
// Ensure the store directory exists and is writable before creating the raft group
if err := os.MkdirAll(cfg.Store, defaultDirPerms); err != nil {
return fmt.Errorf("store dir %s not creatable: %w", cfg.Store, err)
}
probe, err := os.CreateTemp(cfg.Store, "_probe_")
if err != nil { return err }
probe.Close(); os.Remove(probe.Name()) Type guard
func storeDirWritable(dir string) bool {
if st, err := os.Stat(dir); err == nil && !st.IsDir() { return false }
f, err := os.CreateTemp(dir, "_w_")
if err != nil { return false }
f.Close(); os.Remove(f.Name())
return true
} Try / catch
if _, err := createRaftGroup(cfg); err != nil {
if strings.Contains(err.Error(), "storage directory") {
return fmt.Errorf("check permissions/ownership/space on %s (run as the server user, not read-only): %w", cfg.Store, err)
}
return err
} Prevention
- Run the server as a user with write access to store_dir.
- Avoid read-only mounts for JetStream/raft data volumes.
- Monitor disk space; ENOSPC surfaces as this error.
- Verify the configured path is a directory, not a regular file.
When it happens
Trigger: Creating a raft group with a file-based (non-memory) WAL whose Store directory cannot be created because the parent path is unwritable, a file already occupies the path, or the filesystem is read-only/full.
Common situations: Store dir under a root-owned path while the server runs unprivileged, container volumes mounted read-only, typo'd store_dir path, or disk exhaustion on the JetStream data volume.
Related errors
- could not create storage directory - %v
- storage directory is not a directory
- storage directory is not writable
- could not create snapshots directory - %v
- storage type can not be updated
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/a13b376351903010.
Report an issue: GitHub.