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
- Create the directory manually with correct ownership: mkdir -p <store_dir> && chown nats:nats <store_dir>
- Check the filesystem is writable and mounted (df/mount) before starting the server
- Fix store_dir in config to a valid writable path
- 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
- Pre-create store_dir in deployment tooling with correct ownership
- Verify the path's mount exists and is writable before service start
- Run the server under a dedicated user with write access to the store path
- In containers, mount a volume at the store path instead of using container-local FS
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
- storage directory is not writable
- could not create storage directory - %v
- storage directory is not writable
- could not create message storage directory - %v
- could not create consumer storage directory - %v
AI-assisted analysis of nats-io/nats-server@3a66a489d2 (2026-09-02).
Data as JSON: /api/errors/3f9c882bf84d1caa.
Report an issue: GitHub.