diff --git a/src/tap/formatter/FITSFormat.java b/src/tap/formatter/FITSFormat.java
index 13c5b85e06e3a0dda7d549ecead3ce063703b43c..212cfc90731cc35d39f341d392a834d69bd0b6c4 100644
--- a/src/tap/formatter/FITSFormat.java
+++ b/src/tap/formatter/FITSFormat.java
@@ -2,21 +2,22 @@ package tap.formatter;
 
 /*
  * This file is part of TAPLibrary.
- * 
+ *
  * TAPLibrary is free software: you can redistribute it and/or modify
  * it under the terms of the GNU Lesser General Public License as published by
  * the Free Software Foundation, either version 3 of the License, or
  * (at your option) any later version.
- * 
+ *
  * TAPLibrary is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU Lesser General Public License for more details.
- * 
+ *
  * 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 2014-2015 - Astronomisches Rechen Institut (ARI)
+ *
+ * Copyright 2014-2020 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ *                       Astronomisches Rechen Institut (ARI)
  */
 
 import java.io.IOException;
@@ -34,24 +35,27 @@ import uk.ac.starlink.table.StoragePolicy;
 
 /**
  * Format any given query (table) result into FITS.
- * 
- * @author Gr&eacute;gory Mantelet (ARI)
- * @version 2.1 (11/2015)
+ *
+ * @author Gr&eacute;gory Mantelet (CDS;ARI)
+ * @version 2.4 (08/2020)
  * @since 2.0
  */
 public class FITSFormat implements OutputFormat {
 
-	/** The {@link ServiceConnection} to use (for the log and to have some information about the service (particularly: name, description). */
+	/** The {@link ServiceConnection} to use (for the log and to have some
+	 * information about the service (particularly: name, description). */
 	protected final ServiceConnection service;
 
 	/**
 	 * Creates a FITS formatter.
-	 * 
-	 * @param service	The service to use (for the log and to have some information about the service (particularly: name, description).
-	 * 
-	 * @throws NullPointerException	If the given service connection is <code>null</code>.
+	 *
+	 * @param service	The service to use (for the log and to have some
+	 *               	information about the service (particularly: name,
+	 *               	description).
+	 *
+	 * @throws NullPointerException	If the given service connection is NULL.
 	 */
-	public FITSFormat(final ServiceConnection service) throws NullPointerException{
+	public FITSFormat(final ServiceConnection service) throws NullPointerException {
 		if (service == null)
 			throw new NullPointerException("The given service connection is NULL !");
 
@@ -59,27 +63,27 @@ public class FITSFormat implements OutputFormat {
 	}
 
 	@Override
-	public String getMimeType(){
+	public String getMimeType() {
 		return "application/fits";
 	}
 
 	@Override
-	public String getShortMimeType(){
+	public String getShortMimeType() {
 		return "fits";
 	}
 
 	@Override
-	public String getDescription(){
+	public String getDescription() {
 		return null;
 	}
 
 	@Override
-	public String getFileExtension(){
+	public String getFileExtension() {
 		return "fits";
 	}
 
 	@Override
-	public void writeResult(TableIterator result, OutputStream output, TAPExecutionReport execReport, Thread thread) throws TAPException, IOException, InterruptedException{
+	public void writeResult(TableIterator result, OutputStream output, TAPExecutionReport execReport, Thread thread) throws TAPException, IOException, InterruptedException {
 		// Extract the columns' metadata:
 		ColumnInfo[] colInfos = VOTableFormat.toColumnInfos(result, execReport, thread);
 
@@ -87,7 +91,20 @@ public class FITSFormat implements OutputFormat {
 		LimitedStarTable table = new LimitedStarTable(result, colInfos, execReport.parameters.getMaxRec(), thread);
 
 		// Copy the table on disk (or in memory if the table is short):
-		StarTable copyTable = StoragePolicy.PREFER_DISK.copyTable(table);
+		StarTable copyTable;
+		try {
+			copyTable = StoragePolicy.PREFER_DISK.copyTable(table);
+		} catch(IOException ioe) {
+			/* In case of time out, LimitedStarTable makes copyTable to stop by
+			 * throwing an IOException. In such case, this IOException has to be
+			 * interpreted as a normal interruption: */
+			if (thread.isInterrupted())
+				throw new InterruptedException();
+			/* Otherwise, the error has to be managed properly (so, wrap it
+			 * inside a TAPException): */
+			else
+				throw new TAPException("Unexpected error while formatting the result!", ioe);
+		}
 
 		if (thread.isInterrupted())
 			throw new InterruptedException();
diff --git a/src/tap/formatter/VOTableFormat.java b/src/tap/formatter/VOTableFormat.java
index 7252cb8486577dfd846e26ce663daebd16c49422..9360e6dcc410c555090c31bfdea84c3eff06d414 100644
--- a/src/tap/formatter/VOTableFormat.java
+++ b/src/tap/formatter/VOTableFormat.java
@@ -16,7 +16,7 @@ package tap.formatter;
  * 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 2012-2019 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
+ * Copyright 2012-2020 - UDS/Centre de Données astronomiques de Strasbourg (CDS)
  *                       Astronomisches Rechen Institut (ARI)
  */
 
@@ -54,49 +54,70 @@ import uk.ac.starlink.votable.VOStarTable;
 import uk.ac.starlink.votable.VOTableVersion;
 
 /**
- * <p>Format any given query (table) result into VOTable.</p>
+ * Format any given query (table) result into VOTable.
  *
  * <p>
- * 	Format and version of the resulting VOTable can be provided in parameters at the construction time.
- * 	This formatter is using STIL. So all formats and versions managed by STIL are also here.
- * 	Basically, you have the following formats: TABLEDATA, BINARY, BINARY2 (only when using VOTable v1.3) and FITS.
+ * 	Format and version of the resulting VOTable can be provided in parameters at
+ * 	the construction time. This formatter is using STIL. So all formats and
+ * 	versions managed by STIL are also here. Basically, you have the following
+ * 	formats: TABLEDATA, BINARY, BINARY2 (only when using VOTable v1.3) and FITS.
  * 	The versions are: 1.0, 1.1, 1.2 and 1.3.
  * </p>
  *
- * <p>Note: The MIME type is automatically set in function of the given VOTable serialization:</p>
+ * <p><i><b>Note:</b>
+ * 	The MIME type is automatically set in function of the given VOTable
+ * 	serialization:
+ * </i></p>
  * <ul>
  * 	<li><b>none or unknown</b>: equivalent to BINARY</li>
- * 	<li><b>BINARY</b>:          "application/x-votable+xml" = "votable"</li>
- * 	<li><b>BINARY2</b>:         "application/x-votable+xml;serialization=BINARY2" = "votable/b2"</li>
- * 	<li><b>TABLEDATA</b>:       "application/x-votable+xml;serialization=TABLEDATA" = "votable/td"</li>
- * 	<li><b>FITS</b>:            "application/x-votable+xml;serialization=FITS" = "votable/fits"</li>
+ * 	<li><b>BINARY</b>:          "application/x-votable+xml"
+ *                              = "votable"</li>
+ * 	<li><b>BINARY2</b>:         "application/x-votable+xml;serialization=BINARY2"
+ *                              = "votable/b2"</li>
+ * 	<li><b>TABLEDATA</b>:       "application/x-votable+xml;serialization=TABLEDATA"
+ *                              = "votable/td"</li>
+ * 	<li><b>FITS</b>:            "application/x-votable+xml;serialization=FITS"
+ *                              = "votable/fits"</li>
  * </ul>
- * <p>It is however possible to change these default values thanks to {@link #setMimeType(String, String)}.</p>
+ * <p>
+ * 	It is however possible to change these default values thanks to
+ * 	{@link #setMimeType(String, String)}.
+ * </p>
  *
- * <p>In addition of the INFO elements for QUERY_STATUS="OK" and QUERY_STATUS="OVERFLOW", two additional INFO elements are written:</p>
+ * <p>
+ * 	In addition of the INFO elements for QUERY_STATUS="OK",
+ * 	QUERY_STATUS="OVERFLOW" and QUERY_STATUS="ERROR", two additional INFO
+ * 	elements are written:
+ * </p>
  * <ul>
- * 	<li>PROVIDER = {@link ServiceConnection#getProviderName()} and {@link ServiceConnection#getProviderDescription()}</li>
+ * 	<li>PROVIDER = {@link ServiceConnection#getProviderName()} and
+ *      {@link ServiceConnection#getProviderDescription()}</li>
  * 	<li>QUERY = the ADQL query at the origin of this result.</li>
  * </ul>
  *
  * <p>
- * 	Furthermore, this formatter provides a function to format an error in VOTable: {@link #writeError(String, Map, PrintWriter)}.
- * 	This is useful for TAP which requires to return in VOTable any error that occurs while any operation.
- * 	<i>See {@link DefaultTAPErrorWriter} for more details.</i>
+ * 	Furthermore, this formatter provides a function to format an error in
+ * 	VOTable: {@link #writeError(String, Map, PrintWriter)}. This is useful for
+ * 	TAP which requires to return in VOTable any error that occurs while any
+ * 	operation. <i>See {@link DefaultTAPErrorWriter} for more details.</i>
  * </p>
  *
  * @author Gr&eacute;gory Mantelet (CDS;ARI)
- * @version 2.3 (03/2019)
+ * @version 2.4 (08/2020)
  */
 public class VOTableFormat implements OutputFormat {
 
-	/** The {@link ServiceConnection} to use (for the log and to have some information about the service (particularly: name, description). */
+	/** The {@link ServiceConnection} to use (for the log and to have some
+	 * information about the service (particularly: name, description). */
 	protected final ServiceConnection service;
 
-	/** Format of the VOTable data part in which data must be formatted. Possible values are: TABLEDATA, BINARY, BINARY2 or FITS. By default, it is set to BINARY. */
+	/** Format of the VOTable data part in which data must be formatted.
+	 * Possible values are: TABLEDATA, BINARY, BINARY2 or FITS.
+	 * By default, it is set to BINARY. */
 	protected final DataFormat votFormat;
 
-	/** VOTable version in which table data must be formatted. By default, it is set to v13. */
+	/** VOTable version in which table data must be formatted.
+	 * By default, it is set to v13. */
 	protected final VOTableVersion votVersion;
 
 	/** MIME type associated with this format. */
@@ -106,63 +127,92 @@ public class VOTableFormat implements OutputFormat {
 	protected String shortMimeType;
 
 	/**
-	 * <p>Creates a VOTable formatter.</p>
+	 * Creates a VOTable formatter.
 	 *
-	 * <p><i>Note:
-	 * 	The MIME type is automatically set to "application/x-votable+xml" = "votable".
-	 * 	It is however possible to change this default value thanks to {@link #setMimeType(String, String)}.
+	 * <p><i><b>Note:</b>
+	 * 	The MIME type is automatically set to "application/x-votable+xml" =
+	 * 	"votable". It is however possible to change this default value thanks to
+	 * 	{@link #setMimeType(String, String)}.
 	 * </i></p>
 	 *
-	 * @param service				The service to use (for the log and to have some information about the service (particularly: name, description).
+	 * @param service	The service to use (for the log and to have some
+	 *               	information about the service (particularly: name,
+	 *               	description).
 	 *
-	 * @throws NullPointerException	If the given service connection is <code>null</code>.
+	 * @throws NullPointerException	If the given service connection is NULL.
 	 */
-	public VOTableFormat(final ServiceConnection service) throws NullPointerException{
+	public VOTableFormat(final ServiceConnection service) throws NullPointerException {
 		this(service, null, null);
 	}
 
 	/**
-	 * <p>Creates a VOTable formatter.</p>
+	 * Creates a VOTable formatter.
 	 *
-	 * <i>Note: The MIME type is automatically set in function of the given VOTable serialization:</i>
+	 * <p><i><b>Note:</b>
+	 * 	The MIME type is automatically set in function of the given VOTable
+	 * 	serialization:
+	 * </i></p>
 	 * <ul>
 	 * 	<li><i><b>none or unknown</b>: equivalent to BINARY</i></li>
-	 * 	<li><i><b>BINARY</b>:          "application/x-votable+xml" = "votable"</i></li>
-	 * 	<li><i><b>BINARY2</b>:         "application/x-votable+xml;serialization=BINARY2" = "votable/b2"</i></li>
-	 * 	<li><i><b>TABLEDATA</b>:       "application/x-votable+xml;serialization=TABLEDATA" = "votable/td"</i></li>
-	 * 	<li><i><b>FITS</b>:            "application/x-votable+xml;serialization=FITS" = "votable/fits"</i></li>
+	 * 	<li><i><b>BINARY</b>:          "application/x-votable+xml"
+	 *                                 = "votable"</i></li>
+	 * 	<li><i><b>BINARY2</b>:         "application/x-votable+xml;serialization=BINARY2"
+	 *                                 = "votable/b2"</i></li>
+	 * 	<li><i><b>TABLEDATA</b>:       "application/x-votable+xml;serialization=TABLEDATA"
+	 *                                 = "votable/td"</i></li>
+	 * 	<li><i><b>FITS</b>:            "application/x-votable+xml;serialization=FITS"
+	 *                                 = "votable/fits"</i></li>
 	 * </ul>
-	 * <p><i>It is however possible to change these default values thanks to {@link #setMimeType(String, String)}.</i></p>
+	 * <p><i>
+	 * 	It is however possible to change these default values thanks to
+	 * 	{@link #setMimeType(String, String)}.
+	 * </i></p>
 	 *
-	 * @param service				The service to use (for the log and to have some information about the service (particularly: name, description).
-	 * @param votFormat				Serialization of the VOTable data part. (TABLEDATA, BINARY, BINARY2 or FITS).
+	 * @param service	The service to use (for the log and to have some
+	 *                  information about the service (particularly: name,
+	 *                  description).
+	 * @param votFormat	Serialization of the VOTable data part.
+	 *                  (TABLEDATA, BINARY, BINARY2 or FITS).
 	 *
-	 * @throws NullPointerException	If the given service connection is <code>null</code>.
+	 * @throws NullPointerException	If the given service connection is NULL.
 	 */
-	public VOTableFormat(final ServiceConnection service, final DataFormat votFormat) throws NullPointerException{
+	public VOTableFormat(final ServiceConnection service, final DataFormat votFormat) throws NullPointerException {
 		this(service, votFormat, null);
 	}
 
 	/**
-	 * <p>Creates a VOTable formatter.</p>
+	 * Creates a VOTable formatter.
 	 *
-	 * <i>Note: The MIME type is automatically set in function of the given VOTable serialization:</i>
+	 * <p><i><b>Note:</b>
+	 * 	The MIME type is automatically set in function of the given VOTable
+	 * 	serialization:
+	 * </i></p>
 	 * <ul>
 	 * 	<li><i><b>none or unknown</b>: equivalent to BINARY</i></li>
-	 * 	<li><i><b>BINARY</b>:          "application/x-votable+xml" = "votable"</i></li>
-	 * 	<li><i><b>BINARY2</b>:         "application/x-votable+xml;serialization=BINARY2" = "votable/b2"</i></li>
-	 * 	<li><i><b>TABLEDATA</b>:       "application/x-votable+xml;serialization=TABLEDATA" = "votable/td"</i></li>
-	 * 	<li><i><b>FITS</b>:            "application/x-votable+xml;serialization=FITS" = "votable/fits"</i></li>
+	 * 	<li><i><b>BINARY</b>:          "application/x-votable+xml"
+	 *                                 = "votable"</i></li>
+	 * 	<li><i><b>BINARY2</b>:         "application/x-votable+xml;serialization=BINARY2"
+	 *                                 = "votable/b2"</i></li>
+	 * 	<li><i><b>TABLEDATA</b>:       "application/x-votable+xml;serialization=TABLEDATA"
+	 *                                 = "votable/td"</i></li>
+	 * 	<li><i><b>FITS</b>:            "application/x-votable+xml;serialization=FITS"
+	 *                                 = "votable/fits"</i></li>
 	 * </ul>
-	 * <p><i>It is however possible to change these default values thanks to {@link #setMimeType(String, String)}.</i></p>
+	 * <p><i>
+	 * 	It is however possible to change these default values thanks to
+	 * 	{@link #setMimeType(String, String)}.
+	 * </i></p>
 	 *
-	 * @param service				The service to use (for the log and to have some information about the service (particularly: name, description).
-	 * @param votFormat				Serialization of the VOTable data part. (TABLEDATA, BINARY, BINARY2 or FITS).
-	 * @param votVersion			Version of the resulting VOTable.
+	 * @param service		The service to use (for the log and to have some
+	 *                  	information about the service (particularly: name,
+	 *                  	description).
+	 * @param votFormat		Serialization of the VOTable data part.
+	 *                  	(TABLEDATA, BINARY, BINARY2 or FITS).
+	 * @param votVersion	Version of the resulting VOTable.
 	 *
-	 * @throws NullPointerException	If the given service connection is <code>null</code>.
+	 * @throws NullPointerException	If the given service connection is NULL.
 	 */
-	public VOTableFormat(final ServiceConnection service, final DataFormat votFormat, final VOTableVersion votVersion) throws NullPointerException{
+	public VOTableFormat(final ServiceConnection service, final DataFormat votFormat, final VOTableVersion votVersion) throws NullPointerException {
 		if (service == null)
 			throw new NullPointerException("The given service connection is NULL!");
 
@@ -173,43 +223,47 @@ public class VOTableFormat implements OutputFormat {
 		this.votVersion = (votVersion == null) ? VOTableVersion.V13 : votVersion;
 
 		// Deduce automatically the MIME type and its short expression:
-		if (this.votFormat.equals(DataFormat.BINARY)){
+		if (this.votFormat.equals(DataFormat.BINARY)) {
 			this.mimeType = "application/x-votable+xml";
 			this.shortMimeType = "votable";
-		}else if (this.votFormat.equals(DataFormat.BINARY2)){
+		} else if (this.votFormat.equals(DataFormat.BINARY2)) {
 			this.mimeType = "application/x-votable+xml;serialization=BINARY2";
 			this.shortMimeType = "votable/b2";
-		}else if (this.votFormat.equals(DataFormat.TABLEDATA)){
+		} else if (this.votFormat.equals(DataFormat.TABLEDATA)) {
 			this.mimeType = "application/x-votable+xml;serialization=TABLEDATA";
 			this.shortMimeType = "votable/td";
-		}else if (this.votFormat.equals(DataFormat.FITS)){
+		} else if (this.votFormat.equals(DataFormat.FITS)) {
 			this.mimeType = "application/x-votable+xml;serialization=FITS";
 			this.shortMimeType = "votable/fits";
-		}else{
+		} else {
 			this.mimeType = "application/x-votable+xml";
 			this.shortMimeType = "votable";
 		}
 	}
 
 	@Override
-	public final String getMimeType(){
+	public final String getMimeType() {
 		return mimeType;
 	}
 
 	@Override
-	public final String getShortMimeType(){
+	public final String getShortMimeType() {
 		return shortMimeType;
 	}
 
 	/**
-	 * <p>Set the MIME type associated with this format.</p>
+	 * Set the MIME type associated with this format.
 	 *
-	 * <p><i>Note: NULL means no modification of the current value:</i></p>
+	 * <p><i><b>Note:</b>
+	 * 	NULL means no modification of the current value:
+	 * </i></p>
 	 *
-	 * @param mimeType	Full MIME type of this VOTable format.	<i>note: if NULL, the MIME type is not modified.</i>
-	 * @param shortForm	Short form of this MIME type. <i>note: if NULL, the short MIME type is not modified.</i>
+	 * @param mimeType	Full MIME type of this VOTable format.
+	 *                  <i>note: if NULL, the MIME type is not modified.</i>
+	 * @param shortForm	Short form of this MIME type.
+	 *                  <i>note: if NULL, the short MIME type is not modified.</i>
 	 */
-	public final void setMimeType(final String mimeType, final String shortForm){
+	public final void setMimeType(final String mimeType, final String shortForm) {
 		if (mimeType != null)
 			this.mimeType = mimeType;
 		if (shortForm != null)
@@ -221,7 +275,7 @@ public class VOTableFormat implements OutputFormat {
 	 *
 	 * @return	The data format.
 	 */
-	public final DataFormat getVotSerialization(){
+	public final DataFormat getVotSerialization() {
 		return votFormat;
 	}
 
@@ -230,27 +284,29 @@ public class VOTableFormat implements OutputFormat {
 	 *
 	 * @return	The VOTable version.
 	 */
-	public final VOTableVersion getVotVersion(){
+	public final VOTableVersion getVotVersion() {
 		return votVersion;
 	}
 
 	@Override
-	public String getDescription(){
+	public String getDescription() {
 		return null;
 	}
 
 	@Override
-	public String getFileExtension(){
+	public String getFileExtension() {
 		return "xml";
 	}
 
 	/**
-	 * <p>Write the given error message as VOTable document.</p>
-	 *
-	 * <p><i>Note:
-	 * 	In the TAP protocol, all errors must be returned as VOTable. The class {@link DefaultTAPErrorWriter} is in charge of the management
-	 * 	and reporting of all errors. It is calling this function while the error message to display to the user is ready and
-	 * 	must be written in the HTTP response.
+	 * Write the given error message as VOTable document.
+	 *
+	 * <p><i><b>Note:</b>
+	 * 	In the TAP protocol, all errors must be returned as VOTable. The class
+	 * 	{@link DefaultTAPErrorWriter} is in charge of the management and
+	 * 	reporting of all errors. It is calling this function while the error
+	 * 	message to display to the user is ready and must be written in the HTTP
+	 * 	response.
 	 * </i></p>
 	 *
 	 * <p>Here is the XML format of this VOTable error:</p>
@@ -267,14 +323,15 @@ public class VOTableFormat implements OutputFormat {
 	 * </pre>
 	 *
 	 * @param message	Error message to display to the user.
-	 * @param otherInfo	List of other additional information to display. <i>optional</i>
+	 * @param otherInfo	List of other additional information to display.
+	 *                 	<i>optional</i>
 	 * @param writer	Stream in which the VOTable error must be written.
 	 *
 	 * @throws IOException	If any error occurs while writing in the given output.
 	 *
 	 * @since 2.0
 	 */
-	public void writeError(final String message, final Map<String, String> otherInfo, final PrintWriter writer) throws IOException{
+	public void writeError(final String message, final Map<String, String> otherInfo, final PrintWriter writer) throws IOException {
 		BufferedWriter out = new BufferedWriter(writer);
 
 		// Set the root VOTABLE node:
@@ -292,25 +349,25 @@ public class VOTableFormat implements OutputFormat {
 		out.newLine();
 
 		// Append the PROVIDER information (if any):	[OPTIONAL]
-		if (service.getProviderName() != null){
+		if (service.getProviderName() != null) {
 			out.write("<INFO name=\"PROVIDER\"" + VOSerializer.formatAttribute("value", service.getProviderName()) + ">" + ((service.getProviderDescription() == null) ? "" : VOSerializer.formatText(service.getProviderDescription())) + "</INFO>");
 			out.newLine();
 		}
 
 		// Append the ADQL query at the origin of this result:	[OPTIONAL]
-		if (otherInfo != null){
+		if (otherInfo != null) {
 			Iterator<Map.Entry<String, String>> it = otherInfo.entrySet().iterator();
-			while(it.hasNext()){
+			while(it.hasNext()) {
 				Map.Entry<String, String> entry = it.next();
-				if (entry.getValue() != null){
-					if (entry.getValue().startsWith("\n")){
+				if (entry.getValue() != null) {
+					if (entry.getValue().startsWith("\n")) {
 						int sep = entry.getValue().substring(1).indexOf('\n');
 						if (sep < 0)
 							sep = 0;
 						else
 							sep++;
 						out.write("<INFO " + VOSerializer.formatAttribute("name", entry.getKey()) + VOSerializer.formatAttribute("value", entry.getValue().substring(1, sep)) + ">\n" + entry.getValue().substring(sep + 1) + "\n</INFO>");
-					}else
+					} else
 						out.write("<INFO " + VOSerializer.formatAttribute("name", entry.getKey()) + VOSerializer.formatAttribute("value", entry.getValue()) + "/>");
 					out.newLine();
 				}
@@ -329,7 +386,7 @@ public class VOTableFormat implements OutputFormat {
 	}
 
 	@Override
-	public final void writeResult(final TableIterator queryResult, final OutputStream output, final TAPExecutionReport execReport, final Thread thread) throws TAPException, IOException, InterruptedException{
+	public final void writeResult(final TableIterator queryResult, final OutputStream output, final TAPExecutionReport execReport, final Thread thread) throws TAPException, IOException, InterruptedException {
 		ColumnInfo[] colInfos = toColumnInfos(queryResult, execReport, thread);
 
 		/* Turns the result set into a table. */
@@ -341,29 +398,69 @@ public class VOTableFormat implements OutputFormat {
 		/* if FITS, copy the table on disk (or in memory if the table is short):
 		 * (note: this is needed because STIL needs at least 2 passes on this
 		 *        table to format it correctly in FITS format) */
-		if (votFormat == DataFormat.FITS)
-			voser = VOSerializer.makeSerializer(votFormat, votVersion, StoragePolicy.PREFER_DISK.copyTable(table));
+		if (votFormat == DataFormat.FITS) {
+			try {
+				voser = VOSerializer.makeSerializer(votFormat, votVersion, StoragePolicy.PREFER_DISK.copyTable(table));
+			} catch(IOException ioe) {
+				/* As in the class FITSFormat, the caught IOException may be due
+				 * to an interruption from LimitedStarTable. In such case,
+				 * propagate the interruption: */
+				if (thread.isInterrupted())
+					throw new InterruptedException();
+				/* Any other error should be properly wrapped: */
+				else
+					throw new TAPException("Unexpected error while formatting the result!", ioe);
+			}
+		}
 		// otherwise, just use the default VOTable serializer:
 		else
 			voser = VOSerializer.makeSerializer(votFormat, votVersion, table);
+
 		BufferedWriter out = new BufferedWriter(new OutputStreamWriter(output));
 
 		/* Write header. */
 		writeHeader(votVersion, execReport, out);
 
-		if (thread.isInterrupted())
-			throw new InterruptedException();
-
 		/* Write table element. */
-		voser.writeInlineTableElement(out);
-		execReport.nbRows = table.getNbReadRows();
-		out.flush();
-
-		if (thread.isInterrupted())
-			throw new InterruptedException();
+		if (!thread.isInterrupted()) {
+			try {
+				voser.writeInlineTableElement(out);
+				execReport.nbRows = table.getNbReadRows();
+				out.flush();
+			} catch(Exception ex) {
+				/* If synchronous, the partially written VOTable should be
+				 * properly closed and an error INFO should be appended: */
+				if (execReport.synchronous) {
+					if (votFormat != DataFormat.TABLEDATA) {
+						out.write("</STREAM>\n</BINARY>\n</DATA>\n</TABLE>");
+						out.newLine();
+					}
+					out.write("<INFO name=\"QUERY_STATUS\" value=\"ERROR\">Result truncated due to an unexpected grave error: " + VOSerializer.formatText(ex.getMessage()) + "</INFO>");
+					out.newLine();
+				}
+				// If asynchronous, just propagate the error:
+				else {
+					if (ex instanceof TAPException || ex instanceof IOException || ex instanceof InterruptedException)
+						throw ex;
+					else
+						throw new TAPException(ex);
+				}
+			}
+		}
 
-		/* Check for overflow and write INFO if required. */
-		if (table.lastSequenceOverflowed()){
+		/* If Timed Out... */
+		if (thread.isInterrupted()) {
+			// ...if synchronous, end properly the VOTable with an error INFO:
+			if (execReport != null && execReport.synchronous) {
+				out.write("<INFO name=\"QUERY_STATUS\" value=\"ERROR\">Time out! (Hint: Try running this query in asynchronous mode to get the complete result)</INFO>");
+				out.newLine();
+			}
+			// ...if asynchronous, merely propagate the interruption:
+			else
+				throw new InterruptedException();
+		}
+		/* If Overflow, declare this in an INFO: */
+		else if (table.lastSequenceOverflowed()) {
 			out.write("<INFO name=\"QUERY_STATUS\" value=\"OVERFLOW\"/>");
 			out.newLine();
 		}
@@ -378,16 +475,19 @@ public class VOTableFormat implements OutputFormat {
 	}
 
 	/**
-	 * <p>Writes the first VOTable nodes/elements preceding the data: VOTABLE, RESOURCE and 3 INFOS (QUERY_STATUS, PROVIDER, QUERY).</p>
+	 * Writes the first VOTable nodes/elements preceding the data: VOTABLE,
+	 * RESOURCE and 3 INFOS (QUERY_STATUS, PROVIDER, QUERY).
 	 *
 	 * @param votVersion	Target VOTable version.
 	 * @param execReport	The report of the query execution.
 	 * @param out			Writer in which the root node must be written.
 	 *
-	 * @throws IOException	If there is an error while writing the root node in the given Writer.
-	 * @throws TAPException	If there is any other error (by default: never happen).
+	 * @throws IOException	If there is an error while writing the root node in
+	 *                    	the given Writer.
+	 * @throws TAPException	If there is any other error
+	 *                     	(by default: never happen).
 	 */
-	protected void writeHeader(final VOTableVersion votVersion, final TAPExecutionReport execReport, final BufferedWriter out) throws IOException, TAPException{
+	protected void writeHeader(final VOTableVersion votVersion, final TAPExecutionReport execReport, final BufferedWriter out) throws IOException, TAPException {
 		// Set the root VOTABLE node:
 		out.write("<?xml version=\"1.0\" encoding=\"utf-8\"?>");
 		out.newLine();
@@ -403,34 +503,34 @@ public class VOTableFormat implements OutputFormat {
 		out.newLine();
 
 		// Append the PROVIDER information (if any):	[OPTIONAL]
-		if (service.getProviderName() != null){
+		if (service.getProviderName() != null) {
 			out.write("<INFO name=\"PROVIDER\"" + VOSerializer.formatAttribute("value", service.getProviderName()) + ">" + ((service.getProviderDescription() == null) ? "" : VOSerializer.formatText(service.getProviderDescription())) + "</INFO>");
 			out.newLine();
 		}
 
 		// Append the ADQL query at the origin of this result:	[OPTIONAL]
 		String adqlQuery = execReport.parameters.getQuery();
-		if (adqlQuery != null){
+		if (adqlQuery != null) {
 			out.write("<INFO name=\"QUERY\"" + VOSerializer.formatAttribute("value", adqlQuery) + "/>");
 			out.newLine();
 		}
 
 		// Append the fixed ADQL query, if any:	[OPTIONAL]
 		String fixedQuery = execReport.fixedQuery;
-		if (fixedQuery != null){
+		if (fixedQuery != null) {
 			out.write("<INFO name=\"QUERY_AFTER_AUTO_FIX\"" + VOSerializer.formatAttribute("value", fixedQuery) + "/>");
 			out.newLine();
 		}
 
 		// Insert the definition of all used coordinate systems:
 		HashSet<String> insertedCoosys = new HashSet<String>(10);
-		for(DBColumn col : execReport.resultingColumns){
+		for(DBColumn col : execReport.resultingColumns) {
 			// ignore columns with no coossys:
-			if (col instanceof TAPColumn && ((TAPColumn)col).getCoosys() != null){
+			if (col instanceof TAPColumn && ((TAPColumn)col).getCoosys() != null) {
 				// get its coosys:
 				TAPCoosys coosys = ((TAPColumn)col).getCoosys();
 				// insert the coosys definition ONLY if not already done because of another column:
-				if (!insertedCoosys.contains(coosys.getId())){
+				if (!insertedCoosys.contains(coosys.getId())) {
 					// write the VOTable serialization of this coordinate system definition:
 					out.write("<COOSYS" + VOSerializer.formatAttribute("ID", coosys.getId()));
 					if (coosys.getSystem() != null)
@@ -453,17 +553,21 @@ public class VOTableFormat implements OutputFormat {
 	/**
 	 * Writes fields' metadata of the given query result.
 	 *
-	 * @param result		The query result from whose fields' metadata must be written.
+	 * @param result		The query result from whose fields' metadata must be
+	 *              		written.
 	 * @param execReport	The report of the query execution.
 	 * @param thread		The thread which asked for the result writing.
 	 *
-	 * @return				Extracted field's metadata, or NULL if no metadata have been found (theoretically, it never happens).
+	 * @return	Extracted field's metadata, or NULL if no metadata have been
+	 *        	found (theoretically, it never happens).
 	 *
-	 * @throws IOException				If there is an error while writing the metadata.
-	 * @throws TAPException				If there is any other error.
-	 * @throws InterruptedException		If the given thread has been interrupted.
+	 * @throws IOException			If there is an error while writing the
+	 *                    			metadata.
+	 * @throws TAPException			If there is any other error.
+	 * @throws InterruptedException	If the given thread has been
+	 *                             	interrupted.
 	 */
-	public static final ColumnInfo[] toColumnInfos(final TableIterator result, final TAPExecutionReport execReport, final Thread thread) throws IOException, TAPException, InterruptedException{
+	public static final ColumnInfo[] toColumnInfos(final TableIterator result, final TAPExecutionReport execReport, final Thread thread) throws IOException, TAPException, InterruptedException {
 		// Get the metadata extracted/guesses from the ADQL query:
 		DBColumn[] columnsFromQuery = execReport.resultingColumns;
 
@@ -471,13 +575,13 @@ public class VOTableFormat implements OutputFormat {
 		TAPColumn[] columnsFromResult = result.getMetadata();
 
 		int indField = 0;
-		if (columnsFromQuery != null){
+		if (columnsFromQuery != null) {
 
 			// Initialize the resulting array:
 			ColumnInfo[] colInfos = new ColumnInfo[columnsFromQuery.length];
 
 			// For each column:
-			for(DBColumn field : columnsFromQuery){
+			for(DBColumn field : columnsFromQuery) {
 
 				// Try to build/get appropriate metadata for this field/column:
 				TAPColumn colFromResult = (columnsFromResult != null && indField < columnsFromResult.length) ? columnsFromResult[indField] : null;
@@ -490,41 +594,43 @@ public class VOTableFormat implements OutputFormat {
 			}
 
 			return colInfos;
-		}else
+		} else
 			return null;
 	}
 
 	/**
-	 * Try to get or otherwise to build appropriate metadata using those extracted from the ADQL query and those extracted from the result.
+	 * Try to get or otherwise to build appropriate metadata using those
+	 * extracted from the ADQL query and those extracted from the result.
 	 *
 	 * @param typeFromQuery		Metadata extracted/guessed from the ADQL query.
 	 * @param typeFromResult	Metadata extracted/guessed from the result.
 	 *
 	 * @return	The most appropriate metadata.
 	 */
-	protected static final TAPColumn getValidColMeta(final DBColumn typeFromQuery, final TAPColumn typeFromResult){
-		if (typeFromQuery != null && typeFromQuery instanceof TAPColumn){
+	protected static final TAPColumn getValidColMeta(final DBColumn typeFromQuery, final TAPColumn typeFromResult) {
+		if (typeFromQuery != null && typeFromQuery instanceof TAPColumn) {
 			TAPColumn colMeta = (TAPColumn)typeFromQuery;
 			if (colMeta.getDatatype().isUnknown() && typeFromResult != null && !typeFromResult.getDatatype().isUnknown())
 				colMeta.setDatatype(typeFromResult.getDatatype());
 			return colMeta;
-		}else if (typeFromResult != null){
+		} else if (typeFromResult != null) {
 			if (typeFromQuery != null)
 				return (TAPColumn)typeFromResult.copy(typeFromQuery.getDBName(), typeFromQuery.getADQLName(), null);
 			else
 				return (TAPColumn)typeFromResult.copy();
-		}else
+		} else
 			return new TAPColumn((typeFromQuery != null) ? typeFromQuery.getADQLName() : "?", new DBType(DBDatatype.VARCHAR), "?");
 	}
 
 	/**
-	 * Convert the given {@link TAPColumn} object into a {@link ColumnInfo} object.
+	 * Convert the given {@link TAPColumn} object into a {@link ColumnInfo}
+	 * object.
 	 *
 	 * @param tapCol	{@link TAPColumn} to convert into {@link ColumnInfo}.
 	 *
 	 * @return	The corresponding {@link ColumnInfo}.
 	 */
-	protected static final ColumnInfo getColumnInfo(final TAPColumn tapCol){
+	protected static final ColumnInfo getColumnInfo(final TAPColumn tapCol) {
 		// Get the VOTable type:
 		VotType votType = new VotType(tapCol.getDatatype());
 
@@ -554,19 +660,20 @@ public class VOTableFormat implements OutputFormat {
 	}
 
 	/**
-	 * Convert the VOTable datatype string into a corresponding {@link Class} object.
+	 * Convert the VOTable datatype string into a corresponding {@link Class}
+	 * object.
 	 *
 	 * @param datatype	Value of the VOTable attribute "datatype".
 	 * @param arraysize	Value of the VOTable attribute "arraysize".
 	 *
 	 * @return	The corresponding {@link Class} object.
 	 */
-	protected static final Class<?> getDatatypeClass(final VotDatatype datatype, final String arraysize){
+	protected static final Class<?> getDatatypeClass(final VotDatatype datatype, final String arraysize) {
 		// Determine whether this type is an array or not:
 		boolean isScalar = arraysize == null || (arraysize.length() == 1 && arraysize.equals("1"));
 
 		// Guess the corresponding Class object (see section "7.1.4 Data Types" of the STIL documentation):
-		switch(datatype){
+		switch(datatype) {
 			case BIT:
 				return boolean[].class;
 			case BOOLEAN:
@@ -601,7 +708,7 @@ public class VOTableFormat implements OutputFormat {
 	 *
 	 * @return	The corresponding {@link ColumnInfo} shape.
 	 */
-	protected static final int[] getShape(final String arraysize){
+	protected static final int[] getShape(final String arraysize) {
 		/*
 		 * Note: multi-dimensional arrays are forbidden in the TAP library,
 		 * so no 'nxm...' is possible.
@@ -616,10 +723,10 @@ public class VOTableFormat implements OutputFormat {
 			return new int[]{ -1 };
 
 		// 'n' => {n}:
-		else{
-			try{
+		else {
+			try {
 				return new int[]{ Integer.parseInt(arraysize) };
-			}catch(NumberFormatException nfe){
+			} catch(NumberFormatException nfe) {
 				// if the given arraysize is incorrect (theoretically, never happens), it is like no arraysize has been provided:
 				return new int[0];
 			}
@@ -627,10 +734,9 @@ public class VOTableFormat implements OutputFormat {
 	}
 
 	/**
-	 * <p>
-	 * 	Special {@link StarTable} able to read a fixed maximum number of rows {@link TableIterator}.
-	 * 	However, if no limit is provided, all rows are read.
-	 * </p>
+	 * Special {@link StarTable} able to read a fixed maximum number of rows
+	 * {@link TableIterator}. However, if no limit is provided, all rows are
+	 * read.
 	 *
 	 * @author Gr&eacute;gory Mantelet (CDS;ARI)
 	 * @version 2.1 (11/2015)
@@ -647,17 +753,21 @@ public class VOTableFormat implements OutputFormat {
 		/** Iterator over the data to read using this special {@link StarTable} */
 		private final TableIterator tableIt;
 
-		/** Thread covering this execution. If it is interrupted, the writing must stop as soon as possible.
+		/** Thread covering this execution. If it is interrupted, the writing
+		 * must stop as soon as possible.
 		 * @since 2.1 */
 		private final Thread threadToWatch;
 
-		/** Limit on the number of rows to read. Over this limit, an "overflow" event occurs and {@link #overflow} is set to TRUE. */
+		/** Limit on the number of rows to read. Over this limit, an "overflow"
+		 * event occurs and {@link #overflow} is set to TRUE. */
 		private final long maxrec;
 
-		/** Indicates whether the maximum allowed number of rows has already been read or not. When true, no more row can be read. */
+		/** Indicates whether the maximum allowed number of rows has already
+		 * been read or not. When true, no more row can be read. */
 		private boolean overflow;
 
-		/** Last read row. If NULL, no row has been read or no more row is available. */
+		/** Last read row. If NULL, no row has been read or no more row is
+		 * available. */
 		private Object[] row = null;
 
 		/** Number of rows read until now. */
@@ -666,12 +776,15 @@ public class VOTableFormat implements OutputFormat {
 		/**
 		 * Build this special {@link StarTable}.
 		 *
-		 * @param tableIt	Data on which to iterate using this special {@link StarTable}.
+		 * @param tableIt	Data on which to iterate using this special
+		 *               	{@link StarTable}.
 		 * @param colInfos	Information about all columns.
-		 * @param maxrec	Limit on the number of rows to read. <i>(if negative, there will be no limit)</i>
-		 * @param thread	Parent thread. When an interruption is detected the writing must stop as soon as possible.
+		 * @param maxrec	Limit on the number of rows to read.
+		 *              	<i>(if negative, there will be no limit)</i>
+		 * @param thread	Parent thread. When an interruption is detected the
+		 *              	writing must stop as soon as possible.
 		 */
-		LimitedStarTable(final TableIterator tableIt, final ColumnInfo[] colInfos, final long maxrec, final Thread thread){
+		LimitedStarTable(final TableIterator tableIt, final ColumnInfo[] colInfos, final long maxrec, final Thread thread) {
 			this.tableIt = tableIt;
 			this.threadToWatch = thread;
 			nbCol = colInfos.length;
@@ -686,7 +799,7 @@ public class VOTableFormat implements OutputFormat {
 		 *
 		 * @return   true if the last row sequence overflowed
 		 */
-		public boolean lastSequenceOverflowed(){
+		public boolean lastSequenceOverflowed() {
 			return overflow;
 		}
 
@@ -695,27 +808,27 @@ public class VOTableFormat implements OutputFormat {
 		 *
 		 * @return   Number of all read rows.
 		 */
-		public int getNbReadRows(){
+		public int getNbReadRows() {
 			return nbRows;
 		}
 
 		@Override
-		public int getColumnCount(){
+		public int getColumnCount() {
 			return nbCol;
 		}
 
 		@Override
-		public ColumnInfo getColumnInfo(final int colInd){
+		public ColumnInfo getColumnInfo(final int colInd) {
 			return columnInfos[colInd];
 		}
 
 		@Override
-		public long getRowCount(){
+		public long getRowCount() {
 			return -1;
 		}
 
 		@Override
-		public RowSequence getRowSequence() throws IOException{
+		public RowSequence getRowSequence() throws IOException {
 			overflow = false;
 			row = new Object[nbCol];
 
@@ -723,24 +836,24 @@ public class VOTableFormat implements OutputFormat {
 				long irow = -1;
 
 				@Override
-				public boolean next() throws IOException{
+				public boolean next() throws IOException {
 					irow++;
-					try{
-						if (!threadToWatch.isInterrupted() && (maxrec < 0 || irow < maxrec)){
+					try {
+						if (!threadToWatch.isInterrupted() && (maxrec < 0 || irow < maxrec)) {
 							boolean hasNext = tableIt.nextRow();
-							if (hasNext){
+							if (hasNext) {
 								for(int i = 0; i < nbCol && tableIt.hasNextCol(); i++)
 									row[i] = tableIt.nextCol();
 								nbRows++;
-							}else
+							} else
 								row = null;
 							return hasNext;
-						}else{
+						} else {
 							overflow = tableIt.nextRow();
 							row = null;
 							return false;
 						}
-					}catch(DataReadException dre){
+					} catch(DataReadException dre) {
 						if (dre.getCause() != null && dre.getCause() instanceof IOException)
 							throw (IOException)(dre.getCause());
 						else
@@ -749,17 +862,17 @@ public class VOTableFormat implements OutputFormat {
 				}
 
 				@Override
-				public Object[] getRow() throws IOException{
+				public Object[] getRow() throws IOException {
 					return row;
 				}
 
 				@Override
-				public Object getCell(int cellIndex) throws IOException{
+				public Object getCell(int cellIndex) throws IOException {
 					return row[cellIndex];
 				}
 
 				@Override
-				public void close() throws IOException{
+				public void close() throws IOException {
 				}
 			};
 		}