diff --git a/src/tap/data/VOTableIterator.java b/src/tap/data/VOTableIterator.java
index 277b5b024c69eb65ab47cd9e892f4d12dc069610..03bedce77d47130619ffc3f6a6b9a093841e0ec1 100644
--- a/src/tap/data/VOTableIterator.java
+++ b/src/tap/data/VOTableIterator.java
@@ -16,13 +16,16 @@ package tap.data;
  * You should have received a copy of the GNU Lesser General Public License
  * along with TAPLibrary.  If not, see <http://www.gnu.org/licenses/>.
  * 
- * Copyright 2015 - Astronomisches Rechen Institut (ARI)
+ * Copyright 2015-2017 - Astronomisches Rechen Institut (ARI)
  */
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.NoSuchElementException;
 
+import org.xml.sax.SAXParseException;
+
+import adql.db.DBType;
 import tap.TAPException;
 import tap.metadata.TAPColumn;
 import tap.metadata.VotType;
@@ -34,7 +37,6 @@ import uk.ac.starlink.table.StarTableFactory;
 import uk.ac.starlink.table.TableBuilder;
 import uk.ac.starlink.table.TableFormatException;
 import uk.ac.starlink.table.TableSink;
-import adql.db.DBType;
 
 /**
  * <p>{@link TableIterator} which lets iterate over a VOTable input stream using STIL.</p>
@@ -42,7 +44,7 @@ import adql.db.DBType;
  * <p>{@link #getColType()} will return TAP type based on the type declared in the VOTable metadata part.</p>
  * 
  * @author Gr&eacute;gory Mantelet (ARI)
- * @version 2.1 (07/2015)
+ * @version 2.1 (03/2017)
  * @since 2.0
  */
 public class VOTableIterator implements TableIterator {
@@ -61,10 +63,10 @@ public class VOTableIterator implements TableIterator {
 	 * 	Besides, the metadata returned by StarTable are immediately converted into TAP metadata. If this conversion fails, the error is kept
 	 * 	in metaError, so that the VOTable reading can continue if the fact that metadata are missing is not a problem for the class using the
 	 * 	{@link VOTableIterator}.
-	 * </p> 
+	 * </p>
 	 * 
 	 * @author Gr&eacute;gory Mantelet (ARI)
-	 * @version 2.0 (04/2015)
+	 * @version 2.1 (03/2017)
 	 * @since 2.0
 	 */
 	protected static class StreamVOTableSink implements TableSink {
@@ -92,8 +94,52 @@ public class VOTableIterator implements TableIterator {
 		 * 	but no exception should be thrown to VOTableIterator.
 		 * </p>
 		 */
-		public synchronized void stop(){
+		public void stop(){
+			stop(null);
+		}
+
+		/**
+		 * <p>Stop nicely reading the VOTable.</p>
+		 * 
+		 * <p>
+		 * 	An exception will be thrown to the STILTS class using this TableSink,
+		 * 	but no exception should be thrown to VOTableIterator.
+		 * </p>
+		 * 
+		 * @param reason	Reason why this Sink should be stop.
+		 *              	<i>This should be used in case of external grave error that
+		 *              	should be raised when trying to access data through
+		 *              	VOTableIterator.
+		 *              	Example: a wrong VOTable format.</i>
+		 */
+		public synchronized void stop(final Throwable reason){
+			// Prevent further attempt to read the input stream:
 			endReached = true;
+
+			// Set the stop reason (if any):
+			if (reason != null && metaError == null){
+				// Case: Wrong VOTable format:
+				if (reason instanceof TableFormatException){
+					// build the most precise error message as possible:
+					String msg = "The input file is not a valid VOTable document!";
+					if (reason.getCause() != null){
+						if (reason.getCause() instanceof SAXParseException){
+							SAXParseException spe = (SAXParseException)reason.getCause();
+							msg += " Cause: [l." + spe.getLineNumber() + ", c." + spe.getColumnNumber() + "] " + spe.getMessage();
+						}else if (reason.getCause().getMessage() != null)
+							msg += " Cause: " + reason.getCause().getMessage();
+						else
+							msg += " Cause: {" + reason.getCause().getClass().getName() + "}";
+					}
+					// create the exception:
+					metaError = new DataReadException(msg, reason);
+				}
+				// Case: Unknown reason!
+				else if (reason.getMessage() != null && !reason.getMessage().equals(STREAM_ABORTED_MESSAGE))
+					metaError = new DataReadException("Unexpected error while reading the uploaded VOTable!", reason);
+			}
+
+			// Stop waiting (=> the reading is aborted):
 			notifyAll();
 		}
 
@@ -116,7 +162,7 @@ public class VOTableIterator implements TableIterator {
 		@Override
 		public synchronized void acceptRow(final Object[] row) throws IOException{
 			try{
-				// Wait until the last accepted row has been consumed: 
+				// Wait until the last accepted row has been consumed:
 				while(!endReached && pendingRow != null)
 					wait();
 
@@ -188,6 +234,9 @@ public class VOTableIterator implements TableIterator {
 				// If there was an error while interpreting the accepted metadata, throw it:
 				if (metaError != null)
 					throw metaError;
+				// Or if no metadata can be fetched:
+				else if (meta == null || meta.length == 0)
+					throw (metaError = new DataReadException("Unexpected VOTable document: no FIELD can be found!"));
 
 				// Otherwise, just return the metadata:
 				return meta;
@@ -248,7 +297,7 @@ public class VOTableIterator implements TableIterator {
 
 		/**
 		 * Extract an array of {@link TAPColumn} objects. Each corresponds to one of the columns listed in the given table,
-		 * and so corresponds to the metadata of a column. 
+		 * and so corresponds to the metadata of a column.
 		 * 
 		 * @param table		{@link StarTable} which contains only the columns' information.
 		 * 
@@ -359,8 +408,9 @@ public class VOTableIterator implements TableIterator {
 					try{
 						tb.streamStarTable(input, sink, null);
 					}catch(IOException e){
-						if (e.getMessage() != null && !e.getMessage().equals(STREAM_ABORTED_MESSAGE))
-							e.printStackTrace();
+						/* Stop the VOTable sink
+						 *(otherwise it may still waiting for a Thread notification to wake it up): */
+						sink.stop(e);
 					}
 				}
 			};
diff --git a/test/tap/data/TestVOTableIterator.java b/test/tap/data/TestVOTableIterator.java
index 8b405f448f74c7e1bef43bfde2b0276fd81d0719..eba7a547a1e73191c035b1485f30090e8c5a7537 100644
--- a/test/tap/data/TestVOTableIterator.java
+++ b/test/tap/data/TestVOTableIterator.java
@@ -1,6 +1,7 @@
 package tap.data;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -12,6 +13,8 @@ import java.io.InputStream;
 
 import org.junit.Test;
 
+import uk.ac.starlink.table.TableFormatException;
+
 public class TestVOTableIterator {
 
 	public final static String directory = "./test/tap/data/";
@@ -193,4 +196,134 @@ public class TestVOTableIterator {
 			}
 		}
 	}
+
+	@Test
+	public void testWithNotAVotable(){
+		// CASE: Empty file!
+		File emptyFile = new File("test/tap/data/emptyFile");
+		InputStream input = null;
+		VOTableIterator it = null;
+		try{
+			// Create an empty file:
+			emptyFile.createNewFile();
+
+			// Prepare reading it:
+			input = new FileInputStream(emptyFile);
+
+			// Start the iteration:
+			it = new VOTableIterator(input);
+
+			// Try to read the data:
+			it.getMetadata();
+			fail("An exception was expected because the file is empty!");
+
+		}catch(IOException ioe){
+			ioe.printStackTrace();
+			fail("Another exception was expected here! (see the console for more details)");
+		}catch(DataReadException dre){
+			assertNotNull(dre.getCause());
+			assertEquals(TableFormatException.class, dre.getCause().getClass());
+			assertEquals("The input file is not a valid VOTable document! Cause: [l.1, c.1] Premature end of file.", dre.getMessage());
+		}finally{
+			// Close the input stream (if open):
+			if (input != null){
+				try{
+					input.close();
+				}catch(IOException ioe){
+					ioe.printStackTrace();
+				}
+			}
+			// Close the iterator (if any):
+			if (it != null){
+				try{
+					it.close();
+				}catch(DataReadException dre){
+					dre.printStackTrace();
+				}
+			}
+			// Delete the temporary empty file (if exists):
+			if (emptyFile.exists())
+				emptyFile.delete();
+		}
+
+		// CASE: Neither empty nor a VOTable file!
+		File notAVotFile = new File("test/tap/db_testtools/db-test/create-db.sql");
+		input = null;
+		it = null;
+		try{
+			// Prepare reading it:
+			input = new FileInputStream(notAVotFile);
+
+			// Start the iteration:
+			it = new VOTableIterator(input);
+
+			// Try to read the data:
+			it.getMetadata();
+			fail("An exception was expected because the file is not a VOTable!");
+
+		}catch(IOException ioe){
+			ioe.printStackTrace();
+			fail("Another exception was expected here! (see the console for more details)");
+		}catch(DataReadException dre){
+			assertNotNull(dre.getCause());
+			assertEquals(TableFormatException.class, dre.getCause().getClass());
+			assertEquals("The input file is not a valid VOTable document! Cause: [l.2, c.1] Content is not allowed in prolog.", dre.getMessage());
+		}finally{
+			// Close the input stream (if open):
+			if (input != null){
+				try{
+					input.close();
+				}catch(IOException ioe){
+					ioe.printStackTrace();
+				}
+			}
+			// Close the iterator (if any):
+			if (it != null){
+				try{
+					it.close();
+				}catch(DataReadException dre){
+					dre.printStackTrace();
+				}
+			}
+		}
+
+		// CASE: A VOTable with no field declared!
+		File notAValidVotableFile = new File("test/tap/data/testdata_no-field.vot");
+		input = null;
+		it = null;
+		try{
+			// Prepare reading it:
+			input = new FileInputStream(notAValidVotableFile);
+
+			// Start the iteration:
+			it = new VOTableIterator(input);
+
+			// Try to read the data:
+			it.getMetadata();
+			fail("An exception was expected because no FIELD is declared in this VOTable!");
+
+		}catch(IOException ioe){
+			ioe.printStackTrace();
+			fail("Another exception was expected here! (see the console for more details)");
+		}catch(DataReadException dre){
+			assertEquals("Unexpected VOTable document: no FIELD can be found!", dre.getMessage());
+		}finally{
+			// Close the input stream (if open):
+			if (input != null){
+				try{
+					input.close();
+				}catch(IOException ioe){
+					ioe.printStackTrace();
+				}
+			}
+			// Close the iterator (if any):
+			if (it != null){
+				try{
+					it.close();
+				}catch(DataReadException dre){
+					dre.printStackTrace();
+				}
+			}
+		}
+	}
 }