The empty line is added by the the Write CSV activity. You can easily see it by running the activity by itself and then open the csv file in a text editor:
There’re one row with the headers and an empty row. The Append to CSV activity will take this into consideration but the Append Line doesn’t. It will just append a new line below the empty row.
So either use Write CSV with Append to CSV or use Write Text File with Append Line instead.
String.Join(“,”, dtHeaders.Columns.Cast(of DataColumn).Select(Function(c) c.ColumnName))