diff --git a/doc/download.xml b/doc/download.xml
index def4273..cf8548c 100644
--- a/doc/download.xml
+++ b/doc/download.xml
@@ -68,6 +68,8 @@ The following components are supported.
that is a local filename,
and the function writes the downloaded contents to this file;
the returned record does not have a result component in this case.
+
+ If the download fails then this file is not left behind.
verifyCert
-
diff --git a/lib/download.gi b/lib/download.gi
index f3bf36c..4b9a6ce 100644
--- a/lib/download.gi
+++ b/lib/download.gi
@@ -273,6 +273,13 @@ InstallMethod( Download,
if res.success = true then
return res;
fi;
+ # A failed method may have left a partial or bogus target file behind.
+ # Remove it here, so that the guarantee holds for every method,
+ # including ones added to 'Download_Methods' from outside.
+ if IsBound( opt.target ) and IsString( opt.target ) and
+ IsExistingFile( opt.target ) then
+ RemoveFile( opt.target );
+ fi;
Info( InfoUtils, 2, "Download method ", r.name, " failed with\n",
"#I ", res.error );
Add( errors, Concatenation( r.name, ": ", res.error ) );
diff --git a/tst/download.tst b/tst/download.tst
index 5f52849..02856d3 100644
--- a/tst/download.tst
+++ b/tst/download.tst
@@ -152,6 +152,18 @@ gap> RecNames( opt );
[ ]
gap> SetUserPreference( "utils", "DownloadMaxTime", oldpref );
+## A failed download must not leave the target file behind, whichever
+## method was tried. 'Download' is where that is guaranteed: an individual
+## method may well leave a partial file, and one of them has to, since a
+## resuming method needs what the previous attempt wrote.
+gap> file:= Filename( DirectoryTemporary(), "target" );;
+gap> res1:= Download( Concatenation( baseurl, "/missing" ),
+> rec( target:= file ) );;
+gap> res1.success = false;
+true
+gap> IsExistingFile( file );
+false
+
## test errors and redirects
gap> res1:= Download( Concatenation( baseurl, "/missing" ) );;
gap> res1.success = false;