nats-io/nats-server · critical

could not create storage directory - %v

Error message

could not create storage directory - %v

What it means

JetStream enablement: the configured StoreDir does not exist and os.MkdirAll failed (commonly permissions or an invalid path), so the storage directory for streams could not be created.

Source

Thrown at server/jetstream.go:450

	s.gcbMu.Lock()
	if s.gcbOutMax = s.getOpts().JetStreamMaxCatchup; s.gcbOutMax == 0 {
		s.gcbOutMax = defaultMaxTotalCatchupOutBytes
	}
	s.gcbMu.Unlock()

	atomic.StoreInt64(&js.memMax, cfg.MaxMemory)
	atomic.StoreInt64(&js.storeMax, cfg.MaxStore)

	// TODO: Not currently reloadable.
	atomic.StoreInt64(&js.queueLimit, s.getOpts().JetStreamRequestQueueLimit)
	atomic.StoreInt64(&js.infoQueueLimit, s.getOpts().JetStreamInfoQueueLimit)

	s.js.Store(js)

	// FIXME(dlc) - Allow memory only operation?
	if stat, err := os.Stat(cfg.StoreDir); os.IsNotExist(err) {
		if err := os.MkdirAll(cfg.StoreDir, defaultDirPerms); err != nil {
			return fmt.Errorf("could not create storage directory - %v", err)
		}
	} else {
		// Make sure its a directory and that we can write to it.
		if stat == nil || !stat.IsDir() {
			return fmt.Errorf("storage directory is not a directory")
		}
		tmpfile, err := os.CreateTemp(cfg.StoreDir, "_test_")
		if err != nil {
			return fmt.Errorf("storage directory is not writable")
		}
		tmpfile.Close()
		os.Remove(tmpfile.Name())
	}

	if err := s.initJetStreamEncryption(); err != nil {
		return err
	}

View on GitHub (pinned to 3a66a489d2)

Solutions

  1. Create the directory manually with correct ownership: mkdir -p <store_dir> && chown nats:nats <store_dir>
  2. Check the filesystem is writable and mounted (df/mount) before starting the server
  3. Fix store_dir in config to a valid writable path
  4. Check SELinux/AppArmor/deny logs if creation is blocked by policy

Example fix

// before
jetstream { store_dir: "/data/js" } // /data missing or read-only
// after
// mkdir -p /data/js && chown nats:nats /data/js
jetstream { store_dir: "/data/js" }
Defensive patterns

Strategy: validation

Validate before calling

// Before starting the server:
if fi, err := os.Stat(storeDir); err != nil {
    if err := os.MkdirAll(storeDir, 0750); err != nil { return err }
} else if !fi.IsDir() {
    return fmt.Errorf("%s is not a directory", storeDir)
}

Prevention

When it happens

Trigger: First-time JetStream startup where cfg.StoreDir (jetstream { store_dir: ... }) points to a nonexistent directory and mkdir -p style creation fails — e.g. parent dirs missing and not creatable, insufficient permissions, or read-only mount.

Common situations: store_dir under a path the nats-server user can't write; typo'd path on a non-existent mountpoint; container with read-only filesystem; SELinux/AppArmor blocking directory creation.

Related errors


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