{"record":{"id":"c82ee57eef6549c8","repo":"Tencent/WeKnora","slug":"storage-path-is-not-a-directory-s","errorCode":null,"errorMessage":"storage path is not a directory: %s","messagePattern":"storage path is not a directory: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/application/service/file/local.go","lineNumber":33,"sourceCode":"\tsecutils \"github.com/Tencent/WeKnora/internal/utils\"\n)\n\n// localFileService implements the FileService interface for local file system storage\ntype localFileService struct {\n\tbaseDir     string // Base directory for file storage\n\texternalURL string // External URL base for presigned URL generation (empty = return local:// paths)\n}\n\nconst localScheme = \"local://\"\n\n// CheckConnectivity verifies the local storage directory exists and is accessible.\nfunc (s *localFileService) CheckConnectivity(ctx context.Context) error {\n\tinfo, err := os.Stat(s.baseDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"storage directory not accessible: %w\", err)\n\t}\n\tif !info.IsDir() {\n\t\treturn fmt.Errorf(\"storage path is not a directory: %s\", s.baseDir)\n\t}\n\treturn nil\n}\n\n// NewLocalFileService creates a new local file service instance.\n// externalURL is the externally-reachable base URL (e.g. \"https://weknora.example.com\");\n// when set, GetFileURL returns presigned HTTP URLs instead of local:// paths.\nfunc NewLocalFileService(baseDir, externalURL string) interfaces.FileService {\n\treturn &localFileService{\n\t\tbaseDir:     baseDir,\n\t\texternalURL: strings.TrimRight(externalURL, \"/\"),\n\t}\n}\n\n// SaveFile stores an uploaded file to the local file system\n// The file is stored in a directory structure: baseDir/tenantID/knowledgeID/filename\n// Returns the full file path or an error if saving fails\nfunc (s *localFileService) SaveFile(ctx context.Context,","sourceCodeStart":15,"sourceCodeEnd":51,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/application/service/file/local.go#L15-L51","documentation":"localFileService.CheckConnectivity returns \"storage path is not a directory: %s\" when os.Stat succeeds but the target is a regular file, symlink to a file, or other non-directory. The configured baseDir exists but can't serve as a storage root.","triggerScenarios":"baseDir points at an existing regular file — e.g. someone created the path with touch, a config value collides with a file, a bind-mount mounted a file where a directory was expected, or an old symlink now resolves to a file.","commonSituations":"Docker bind-mounting a single file onto the storage path; config pointing at a config file or socket path by mistake; provisioning script using touch instead of mkdir; leftover file from a failed migration.","solutions":["Remove the file at that path and create a directory: rm <path> && mkdir -p <path> (after confirming the file isn't needed).","Fix the storage base-dir config/env to the actual intended directory path.","In Docker/K8s, correct the volume spec so a directory (or emptyDir/PVC) is mounted at the storage path, not a single file.","Fix provisioning/entrypoint scripts to use mkdir -p rather than touch for the storage path."],"exampleFix":"// before\n# provisioning.sh\ntouch /data/weknora/files\n// after\n# provisioning.sh\nmkdir -p /data/weknora/files","handlingStrategy":"validation","validationCode":"info, err := os.Stat(baseDir)\nif err == nil && !info.IsDir() {\n\treturn fmt.Errorf(\"%q exists but is not a directory\", baseDir)\n}","typeGuard":null,"tryCatchPattern":"if err := svc.CheckConnectivity(ctx); err != nil {\n\tif strings.Contains(err.Error(), \"not a directory\") {\n\t\t// operator action required: fix path or replace file with dir\n\t}\n}","preventionTips":["Use mkdir -p, never touch, when provisioning the storage path","Mount directories (PVC/emptyDir), not single files, at the storage path in containers","Keep the storage base dir separate from config/socket paths"],"tags":["filesystem","local-storage","configuration","deployment","go"],"backgroundTag":"storage-path-not-a-directory","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}