netbirdio/netbird · error

failed to create file

Error message

failed to create file

What it means

HTTP 500 returned when os.OpenFile(filePath, O_WRONLY|O_CREATE|O_EXCL, 0600) fails with an error other than EEXIST, after the upload directory was created successfully. The underlying errno is logged as 'Failed to create file <path>' on the server.

Source

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

		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 {
		http.Error(w, "failed to write file", http.StatusInternalServerError)
		log.Errorf("Failed to write file %s: %v", filePath, err)
		return
	}

	log.Infof("Uploaded file %s", filePath)
	w.WriteHeader(http.StatusOK)
}

View on GitHub (pinned to 93e97f4bf1)

Solutions

  1. Read the server log line 'Failed to create file <path>: <err>' and fix per errno: permission denied -> ownership/umask; file name too long -> shorten; is a directory -> rename
  2. Restrict client file names to a safe single segment (letters, digits, '-', '_', '.') before upload
  3. On SELinux/AppArmor systems, verify the process label may write under STORE_DIR (check audit logs)

Example fix

// before: raw client filename used as segment
name := fileHeader.Filename // "../../x" or 300 chars -> 500

// after: sanitize to one safe segment
name := filepath.Base(fileHeader.Filename)
name = strings.Map(func(r rune) rune {
	if unicode.IsLetter(r) || unicode.IsDigit(r) || r == '-' || r == '.' { return r }
	return '_'
}, name)
Defensive patterns

Strategy: try-catch

Validate before calling

// client-side: keep the file segment short and separator-free
name := filepath.Base(fileName)
if len(name) > 128 || strings.ContainsAny(name, "/\\\x00") {
	name = uuid.NewString()
}

Try / catch

On 500 'failed to create file', read the server log for the errno (permission denied, name too long, is a directory), fix the environment or the name, then retry once with a fresh URL from GET /upload-url.

Prevention

When it happens

Trigger: Directory permissions (0750, owner-only) deny create to the process user; the final path component is invalid (contains a decoded '/', a NUL byte, or exceeds NAME_MAX 255); a directory already exists at the exact file path; SELinux/AppArmor denial on the state dir.

Common situations: File names taken straight from client uploads containing separators or extreme length; hardened container security profiles blocking writes outside allowed labels; upload subdirectories created by a different user.

Related errors


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