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

  1. Create/fix the store directory manually and grant write permission to the server's user: `mkdir -p <dir> && chown nats <dir>`.
  2. Check the underlying %v OS error for the precise cause (EACCES, EROFS, ENOSPC).
  3. Point cfg.Store (store_dir) at a writable volume with sufficient disk space.
  4. 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

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


AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02). Data as JSON: /api/errors/a13b376351903010. Report an issue: GitHub.