nats-io/nats-server · critical

could not create storage streams directory - %v

Error message

could not create storage streams directory - %v

What it means

When initializing per-account JetStream storage, the server ensures the <storeDir>/streams directory exists, creating it with defaultDirPerms via os.MkdirAll. If the OS-level mkdir fails (permissions, path conflicts, full disk, not a directory), this error wraps the underlying OS error and aborts account stream storage setup.

Source

Thrown at server/jetstream.go:1292

	s.Debugf("Enabled JetStream for account %q", a.Name)
	if l, ok := limits[_EMPTY_]; ok {
		s.Debugf("  Max Memory:      %s", friendlyBytes(l.MaxMemory))
		s.Debugf("  Max Storage:     %s", friendlyBytes(l.MaxStore))
	} else {
		for t, l := range limits {
			s.Debugf("  Tier: %s", t)
			s.Debugf("    Max Memory:      %s", friendlyBytes(l.MaxMemory))
			s.Debugf("    Max Storage:     %s", friendlyBytes(l.MaxStore))
		}
	}

	// Clean up any old snapshots that were orphaned while staging.
	os.RemoveAll(filepath.Join(js.config.StoreDir, snapStagingDir))

	sdir := filepath.Join(jsa.storeDir, streamsDir)
	if _, err := os.Stat(sdir); os.IsNotExist(err) {
		if err := os.MkdirAll(sdir, defaultDirPerms); err != nil {
			return fmt.Errorf("could not create storage streams directory - %v", err)
		}
		// Just need to make sure we can write to the directory.
		// Remove the directory will create later if needed.
		os.RemoveAll(sdir)
		// when empty remove parent directory, which may have been created as well
		os.Remove(jsa.storeDir)
	} else {
		// Restore any state here.
		s.Debugf("Recovering JetStream state for account %q", a.Name)
	}

	// Remember if we should be encrypted and what cipher we think we should use.
	encrypted := s.getOpts().JetStreamKey != _EMPTY_
	sc := s.getOpts().JetStreamCipher

	doConsumers := func(mset *stream, odir string) {
		ofis, _ := os.ReadDir(odir)
		if len(ofis) > 0 {

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Read the wrapped OS error (%v) to identify the exact filesystem cause.
  2. chown/chmod the jetstream store directory so the server process user can write (defaultDirPerms is typically 0750/0755).
  3. Ensure store_dir path exists, is a directory, and contains no file named 'streams'.
  4. Check disk space and that the filesystem is writable (not read-only mounted).
  5. Fix and restart the server; partially created empty dirs are cleaned up automatically.

Example fix

// before
store_dir: "/nfs/readonly/jetstream"
// after (and on host)
store_dir: "/var/lib/nats/jetstream"
# mkdir -p /var/lib/nats/jetstream && chown nats:nats /var/lib/nats/jetstream
Defensive patterns

Strategy: validation

Validate before calling

sd := storeDir
if fi, err := os.Stat(sd); err != nil || !fi.IsDir() {
    return fmt.Errorf("store_dir %s missing or not a directory: %v", sd, err)
}
probe := filepath.Join(sd, ".write_probe")
if err := os.WriteFile(probe, []byte("ok"), 0600); err != nil {
    return fmt.Errorf("store_dir not writable: %w", err)
}
os.Remove(probe)

Prevention

When it happens

Trigger: Starting a server with jetstream store_dir where os.MkdirAll(jsa.storeDir + '/streams') fails - e.g. permission denied on the store directory, store dir path is a file, read-only filesystem, or disk full.

Common situations: store_dir owned by another user (server run as different UID); container volume mounted read-only; leftover file named 'streams' in the store dir; NFS/permission issues after migrating store directories; full disk after large streams.

Understand the failure class

Background: mkdir permission denied (EACCES): failed to create directory errors explained — this error's family across 32 libraries.

Related errors


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