{"record":{"id":"bd9ca71ed0c76197","repo":"nats-io/nats-server","slug":"storage-directory-is-not-a-directory-bd9ca7","errorCode":null,"errorMessage":"storage directory is not a directory","messagePattern":"storage directory is not a directory","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"server/jetstream.go","lineNumber":455,"sourceCode":"\n\tatomic.StoreInt64(&js.memMax, cfg.MaxMemory)\n\tatomic.StoreInt64(&js.storeMax, cfg.MaxStore)\n\n\t// TODO: Not currently reloadable.\n\tatomic.StoreInt64(&js.queueLimit, s.getOpts().JetStreamRequestQueueLimit)\n\tatomic.StoreInt64(&js.infoQueueLimit, s.getOpts().JetStreamInfoQueueLimit)\n\n\ts.js.Store(js)\n\n\t// FIXME(dlc) - Allow memory only operation?\n\tif stat, err := os.Stat(cfg.StoreDir); os.IsNotExist(err) {\n\t\tif err := os.MkdirAll(cfg.StoreDir, defaultDirPerms); err != nil {\n\t\t\treturn fmt.Errorf(\"could not create storage directory - %v\", err)\n\t\t}\n\t} else {\n\t\t// Make sure its a directory and that we can write to it.\n\t\tif stat == nil || !stat.IsDir() {\n\t\t\treturn fmt.Errorf(\"storage directory is not a directory\")\n\t\t}\n\t\ttmpfile, err := os.CreateTemp(cfg.StoreDir, \"_test_\")\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"storage directory is not writable\")\n\t\t}\n\t\ttmpfile.Close()\n\t\tos.Remove(tmpfile.Name())\n\t}\n\n\tif err := s.initJetStreamEncryption(); err != nil {\n\t\treturn err\n\t}\n\n\t// JetStream is an internal service so we need to make sure we have a system account.\n\t// This system account will export the JetStream service endpoints.\n\tif s.SystemAccount() == nil {\n\t\ts.SetDefaultSystemAccount()\n\t}","sourceCodeStart":437,"sourceCodeEnd":473,"githubUrl":"https://github.com/nats-io/nats-server/blob/3a66a489d262bf89b71a71c955c94920394532f3/server/jetstream.go#L437-L473","documentation":"JetStream enablement: cfg.StoreDir exists but its stat mode is not a directory (a file occupies the path), failing the post-stat check that verifies the storage location is usable as a directory.","triggerScenarios":"cfg.StoreDir points to an existing regular file, symlink to a file, or other non-directory — os.Stat succeeds but stat.IsDir() is false (or stat is nil).","commonSituations":"A file was accidentally created at the store_dir path (e.g. logs or a tarball written there); config pointing at a file like /etc/nats/js.conf instead of a directory; docker volume mounting a file over the dir path.","solutions":["Remove or rename the file occupying store_dir, then create a real directory there","Point store_dir at the intended directory in config","Verify with ls -la <store_dir> that the path is a directory before startup"],"exampleFix":"// before\njetstream { store_dir: \"/var/lib/nats/js\" } // /var/lib/nats/js is a regular file\n// after\n// mv /var/lib/nats/js /var/lib/nats/js.bak && mkdir /var/lib/nats/js && chown nats:nats /var/lib/nats/js","handlingStrategy":"validation","validationCode":"if fi, err := os.Stat(storeDir); err == nil && !fi.IsDir() {\n    return fmt.Errorf(\"%s exists but is not a directory; remove or move it\", storeDir)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never point store_dir at a file path; validate config paths at deploy time","Check that log rotation/backup jobs don't drop files into the store dir","When mounting volumes, ensure the mount source is a directory","Add a pre-start check script: [ -d \"$STORE_DIR\" ] || exit 1"],"tags":["jetstream","storage","filesystem","configuration"],"backgroundTag":"storage-directory-unwritable","analyzedSha":"3a66a489d262bf89b71a71c955c94920394532f3","analyzedAt":"2026-09-02T04:41:54.247Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}