netbirdio/netbird · error

failed to create upload dir

Error message

failed to create upload dir

What it means

HTTP 500 from handlePutRequest when os.MkdirAll(dirPath, 0750) fails while creating the per-id upload directory under the base (STORE_DIR, default /var/lib/netbird). The directory must exist before the file is opened, so any filesystem-level failure at this step aborts the upload; the errno is logged as 'Failed to create upload dir'.

Source

Thrown at upload-server/server/local.go:129

	cleanBase := filepath.Clean(l.dir) + string(filepath.Separator)

	dirPath := filepath.Clean(filepath.Join(l.dir, uploadDir))
	if !strings.HasPrefix(dirPath, cleanBase) {
		http.Error(w, "invalid path", http.StatusBadRequest)
		log.Warnf("Path traversal attempt blocked (dir): %s", dirPath)
		return
	}

	filePath := filepath.Clean(filepath.Join(dirPath, uploadFile))
	if !strings.HasPrefix(filePath, cleanBase) {
		http.Error(w, "invalid path", http.StatusBadRequest)
		log.Warnf("Path traversal attempt blocked (file): %s", filePath)
		return
	}

	if err = os.MkdirAll(dirPath, 0750); err != nil {
		http.Error(w, "failed to create upload dir", http.StatusInternalServerError)
		log.Errorf("Failed to create upload dir: %v", err)
		return
	}

	flags := os.O_WRONLY | os.O_CREATE | os.O_EXCL
	f, err := os.OpenFile(filePath, flags, 0600)
	if err != nil {
		if os.IsExist(err) {
			http.Error(w, "file already exists", http.StatusConflict)
			return
		}
		http.Error(w, "failed to create file", http.StatusInternalServerError)
		log.Errorf("Failed to create file %s: %v", filePath, err)
		return
	}
	defer func() { _ = f.Close() }()

	if _, err = f.Write(body); err != nil {

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Pre-create the base dir owned by the service user: mkdir -p /var/lib/netbird && chown <svcuser> /var/lib/netbird, or point STORE_DIR at a writable absolute path
  2. If the container filesystem is read-only, mount a writable volume at STORE_DIR
  3. Verify no regular file occupies any prefix of the path and check df -h / df -i for space or inode exhaustion

Example fix

# before: read-only container fs, every PUT returns 500 'failed to create upload dir'
docker run -e STORE_DIR=/var/lib/netbird nb/upload-server

# after: writable state dir mounted and owned
docker run -e STORE_DIR=/var/lib/netbird -v nbuploads:/var/lib/netbird nb/upload-server
Defensive patterns

Strategy: validation

Validate before calling

// fail fast at startup instead of 500 on every upload
info, err := os.Stat(storeDir)
if err != nil || !info.IsDir() {
	log.Fatalf("STORE_DIR unusable: %v", err)
}
probe := filepath.Join(storeDir, ".writable-probe")
if f, err := os.OpenFile(probe, os.O_WRONLY|os.O_CREATE, 0600); err != nil {
	log.Fatalf("STORE_DIR not writable: %v", err)
} else {
	_ = f.Close()
	_ = os.Remove(probe)
}

Try / catch

A 500 'failed to create upload dir' is a server-side environment fault; surface it to the operator (it will not self-heal on retry) and stop the upload batch.

Prevention

When it happens

Trigger: Server process runs as a user without write permission on the base dir (a root-owned /var/lib/netbird); STORE_DIR on a read-only volume; a component of STORE_DIR exists as a regular file; disk or inode exhaustion.

Common situations: Running the upload-server container without a writable state volume; deploying as a non-root systemd service without chowning the state dir; Kubernetes pods with readOnlyRootFilesystem and no writable emptyDir mounted at STORE_DIR.

Related errors


AI-assisted analysis of netbirdio/netbird@93e97f4bf1 (2026-08-16). Data as JSON: /api/errors/455c9f78a8f0fbac. Report an issue: GitHub.