Tencent/WeKnora · error

storage path is not a directory: %s

Error message

storage path is not a directory: %s

What it means

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.

Source

Thrown at internal/application/service/file/local.go:33

	secutils "github.com/Tencent/WeKnora/internal/utils"
)

// localFileService implements the FileService interface for local file system storage
type localFileService struct {
	baseDir     string // Base directory for file storage
	externalURL string // External URL base for presigned URL generation (empty = return local:// paths)
}

const localScheme = "local://"

// CheckConnectivity verifies the local storage directory exists and is accessible.
func (s *localFileService) CheckConnectivity(ctx context.Context) error {
	info, err := os.Stat(s.baseDir)
	if err != nil {
		return fmt.Errorf("storage directory not accessible: %w", err)
	}
	if !info.IsDir() {
		return fmt.Errorf("storage path is not a directory: %s", s.baseDir)
	}
	return nil
}

// NewLocalFileService creates a new local file service instance.
// externalURL is the externally-reachable base URL (e.g. "https://weknora.example.com");
// when set, GetFileURL returns presigned HTTP URLs instead of local:// paths.
func NewLocalFileService(baseDir, externalURL string) interfaces.FileService {
	return &localFileService{
		baseDir:     baseDir,
		externalURL: strings.TrimRight(externalURL, "/"),
	}
}

// SaveFile stores an uploaded file to the local file system
// The file is stored in a directory structure: baseDir/tenantID/knowledgeID/filename
// Returns the full file path or an error if saving fails
func (s *localFileService) SaveFile(ctx context.Context,

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Remove the file at that path and create a directory: rm <path> && mkdir -p <path> (after confirming the file isn't needed).
  2. Fix the storage base-dir config/env to the actual intended directory path.
  3. In Docker/K8s, correct the volume spec so a directory (or emptyDir/PVC) is mounted at the storage path, not a single file.
  4. Fix provisioning/entrypoint scripts to use mkdir -p rather than touch for the storage path.

Example fix

// before
# provisioning.sh
touch /data/weknora/files
// after
# provisioning.sh
mkdir -p /data/weknora/files
Defensive patterns

Strategy: validation

Validate before calling

info, err := os.Stat(baseDir)
if err == nil && !info.IsDir() {
	return fmt.Errorf("%q exists but is not a directory", baseDir)
}

Try / catch

if err := svc.CheckConnectivity(ctx); err != nil {
	if strings.Contains(err.Error(), "not a directory") {
		// operator action required: fix path or replace file with dir
	}
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/c82ee57eef6549c8. Report an issue: GitHub.