diff --git a/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java b/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java
index b992f4236a809f36e95dcf6811d6b80ad0675046..6083de9508e852ea2abe0d286a91b4d7ca245a68 100644
--- a/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java
+++ b/ozgcloud-common-lib/src/main/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloader.java
@@ -23,22 +23,26 @@
  */
 package de.ozgcloud.common.binaryfile;
 
-import com.google.protobuf.ByteString;
-import de.ozgcloud.common.errorhandling.TechnicalException;
-import io.grpc.stub.CallStreamObserver;
-import lombok.Builder;
-import lombok.extern.log4j.Log4j2;
-import org.apache.commons.io.IOUtils;
-import org.springframework.core.task.TaskExecutor;
-
 import java.io.IOException;
 import java.io.OutputStream;
 import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
+import java.util.Objects;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
+import org.apache.commons.io.IOUtils;
+import org.springframework.core.task.TaskExecutor;
+
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import io.grpc.stub.CallStreamObserver;
+import lombok.Builder;
+import lombok.extern.log4j.Log4j2;
+
 @Log4j2
 public class GrpcBinaryFileServerDownloader<T> {
 
@@ -49,10 +53,11 @@ public class GrpcBinaryFileServerDownloader<T> {
 	private final Consumer<OutputStream> downloadConsumer;
 	private final TaskExecutor taskExecutor;
 
-	private final byte[] buffer = new byte[GrpcBinaryFileServerDownloader.CHUNK_SIZE];
+	private final byte[] buffer = new byte[CHUNK_SIZE];
 	private final AtomicBoolean started = new AtomicBoolean(false);
 	private final AtomicBoolean downloadFinished = new AtomicBoolean(false);
 	private final AtomicBoolean requestFinished = new AtomicBoolean(false);
+	private final AtomicReference<TechnicalException> downloadError = new AtomicReference<>();
 
 	private PipedInputStream inputStream;
 	private PipedOutputStream outputStream;
@@ -78,19 +83,36 @@ public class GrpcBinaryFileServerDownloader<T> {
 
 	void doStart() {
 		LOG.debug("Starting download.");
-		handleSafety(this::setupStreams);
+		safelySetupStreams();
 		taskExecutor.execute(this::startDownload);
 		callObserver.setOnReadyHandler(this::sendChunks);
 	}
 
+	void safelySetupStreams() {
+		try {
+			setupStreams();
+		} catch (Exception e) {
+			closeOutputStream();
+			closeInputStream();
+			throw new TechnicalException("Error while setting up streams", e);
+		}
+	}
+
 	void setupStreams() throws IOException {
 		outputStream = new PipedOutputStream();
-		inputStream = new PipedInputStream(GrpcBinaryFileServerDownloader.CHUNK_SIZE);
+		inputStream = new PipedInputStream(CHUNK_SIZE);
 		outputStream.connect(inputStream);
 	}
 
 	void startDownload() {
-		handleSafety(this::doDownload);
+		try {
+			doDownload();
+			sendChunks();
+		} catch (Exception e) {
+			downloadError.set(new TechnicalException("Error while downloading file contents", e));
+		} finally {
+			closeOutputStream();
+		}
 	}
 
 	void doDownload() {
@@ -98,12 +120,14 @@ public class GrpcBinaryFileServerDownloader<T> {
 		downloadConsumer.accept(outputStream);
 		LOG.debug("Download completed.");
 		downloadFinished.set(true);
-		closeOutputStream();
-		sendChunks();
 	}
 
 	synchronized void sendChunks() {
-		handleSafety(this::doSendChunks);
+		try {
+			doSendChunks();
+		} catch (Exception e) {
+			completeRequestWithError(new TechnicalException("Error while sending chunks", e));
+		}
 	}
 
 	void doSendChunks() throws IOException {
@@ -111,7 +135,7 @@ public class GrpcBinaryFileServerDownloader<T> {
 			return;
 		}
 		int bytesRead;
-		while (callObserver.isReady()) {
+		while (isReady()) {
 			if ((bytesRead = inputStream.read(buffer)) == -1) {
 				tryCompleteRequest();
 				break;
@@ -121,30 +145,33 @@ public class GrpcBinaryFileServerDownloader<T> {
 		}
 	}
 
+	private boolean isReady() {
+		return callObserver.isReady();
+	}
+
 	void tryCompleteRequest() {
-		if (shouldCompleteRequest()) {
-			completeRequest();
+		if (Objects.nonNull(downloadError.get())) {
+			throw downloadError.get();
+		} else if (downloadFinished.get()) {
+			completeRequestNormally();
 		}
 	}
 
-	boolean shouldCompleteRequest() {
-		return downloadFinished.get() && requestFinished.compareAndSet(false, true);
+	void completeRequestWithError(TechnicalException e) {
+		LOG.debug("Complete download request with error");
+		finishRequest();
+		throw e;
 	}
 
-	void completeRequest() {
+	void completeRequestNormally() {
 		LOG.debug("Complete download request");
-		closeInputStream();
+		finishRequest();
 		callObserver.onCompleted();
 	}
 
-	void handleSafety(ExceptionalRunnable runnable) {
-		try {
-			runnable.run();
-		} catch (Exception e) {
-			closeOutputStream();
-			closeInputStream();
-			throw new TechnicalException("Error occurred during downloading file content download.", e);
-		}
+	private void finishRequest() {
+		requestFinished.set(true);
+		closeInputStream();
 	}
 
 	void closeOutputStream() {
@@ -154,5 +181,4 @@ public class GrpcBinaryFileServerDownloader<T> {
 	void closeInputStream() {
 		IOUtils.closeQuietly(inputStream, e -> LOG.error("InputStream cannot be closed.", e));
 	}
-
 }
\ No newline at end of file
diff --git a/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java b/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java
index 6050e6ba2d06a739e87ec479956dff4fecd707b8..8c3ad5a56eb8a52a3e8b079673406efd68b38420 100644
--- a/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java
+++ b/ozgcloud-common-lib/src/test/java/de/ozgcloud/common/binaryfile/GrpcBinaryFileServerDownloaderTest.java
@@ -23,20 +23,9 @@
  */
 package de.ozgcloud.common.binaryfile;
 
-import com.google.protobuf.ByteString;
-import de.ozgcloud.common.errorhandling.TechnicalException;
-import de.ozgcloud.common.test.ReflectionTestUtils;
-import io.grpc.Context;
-import io.grpc.stub.CallStreamObserver;
-import lombok.SneakyThrows;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Nested;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Captor;
-import org.mockito.Mock;
-import org.springframework.core.task.TaskExecutor;
+import static org.assertj.core.api.Assertions.*;
+import static org.mockito.ArgumentMatchers.*;
+import static org.mockito.Mockito.*;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -45,12 +34,25 @@ import java.io.PipedInputStream;
 import java.io.PipedOutputStream;
 import java.util.Random;
 import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicReference;
 import java.util.function.Consumer;
 import java.util.function.Function;
 
-import static org.assertj.core.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.*;
-import static org.mockito.Mockito.*;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.springframework.core.task.TaskExecutor;
+
+import com.google.protobuf.ByteString;
+
+import de.ozgcloud.common.errorhandling.TechnicalException;
+import de.ozgcloud.common.test.ReflectionTestUtils;
+import io.grpc.stub.CallStreamObserver;
+import lombok.SneakyThrows;
 
 class GrpcBinaryFileServerDownloaderTest {
 
@@ -71,7 +73,6 @@ class GrpcBinaryFileServerDownloaderTest {
 				.chunkBuilder(chunkBuilder).taskExecutor(taskExecutor).build());
 	}
 
-	@DisplayName("Start")
 	@Nested
 	class TestStart {
 
@@ -117,25 +118,23 @@ class GrpcBinaryFileServerDownloaderTest {
 		}
 	}
 
-	@DisplayName("do start")
 	@Nested
 	class TestDoStart {
 
 		@Captor
 		private ArgumentCaptor<Runnable> runnableCaptor;
-		@Captor
-		private ArgumentCaptor<ExceptionalRunnable> setupStreamCaptor;
+
+		@BeforeEach
+		void init() {
+			doNothing().when(downloader).safelySetupStreams();
+		}
 
 		@SneakyThrows
 		@Test
-		void shouldCallSetupStreams() {
-			doNothing().when(downloader).handleSafety(any());
-
+		void shouldSafelySetupStreams() {
 			downloader.doStart();
 
-			verify(downloader).handleSafety(setupStreamCaptor.capture());
-			setupStreamCaptor.getValue().run();
-			verify(downloader).setupStreams();
+			verify(downloader).safelySetupStreams();
 		}
 
 		@Test
@@ -159,28 +158,138 @@ class GrpcBinaryFileServerDownloaderTest {
 		}
 	}
 
-	@DisplayName("Start download")
+	@Nested
+	class TestSafelySetupStreams {
+
+		@SneakyThrows
+		@Test
+		void shouldSetupStreams() {
+			doNothing().when(downloader).setupStreams();
+
+			safelySetupStreams();
+
+			verify(downloader).setupStreams();
+		}
+
+		@Nested
+		class OnException {
+
+			@Mock
+			private PipedOutputStream outputStream;
+			@Mock
+			private PipedInputStream inputStream;
+
+			private final IOException exception = new IOException();
+
+			@SneakyThrows
+			@BeforeEach
+			void init() {
+				setInputStreamField(inputStream);
+				setOutputStreamField(outputStream);
+				doThrow(exception).when(downloader).setupStreams();
+			}
+
+			@Test
+			void shouldThrowBinaryFileDownloadException() {
+				assertThatThrownBy(() -> downloader.safelySetupStreams()).isInstanceOf(TechnicalException.class).hasCause(exception);
+			}
+
+			@SneakyThrows
+			@Test
+			void shouldCloseOutputStream() {
+				catchThrowable(TestSafelySetupStreams.this::safelySetupStreams);
+
+				verify(outputStream).close();
+			}
+
+			@SneakyThrows
+			@Test
+			void shouldCloseInputStream() {
+				catchThrowable(TestSafelySetupStreams.this::safelySetupStreams);
+
+				verify(inputStream).close();
+			}
+		}
+
+		private void safelySetupStreams() {
+			downloader.safelySetupStreams();
+		}
+	}
+
 	@Nested
 	class TestStartDownload {
 
-		@Captor
-		private ArgumentCaptor<ExceptionalRunnable> runnableCaptor;
+		@Mock
+		private PipedOutputStream outputStream;
+
+		@BeforeEach
+		void init() {
+			setOutputStreamField(outputStream);
+		}
 
-		@SneakyThrows
 		@Test
-		void shouldCallDoDownload() {
-			doNothing().when(downloader).handleSafety(any());
-			doNothing().when(downloader).doDownload();
+		void shouldErrorBeInitiallyNull() {
+			assertThat(getDownloadError()).isNull();
+		}
 
-			downloader.startDownload();
+		@Nested
+		class OnNoException {
 
-			verify(downloader).handleSafety(runnableCaptor.capture());
-			runnableCaptor.getValue().run();
-			verify(downloader).doDownload();
+			@BeforeEach
+			void init() {
+				doNothing().when(downloader).doDownload();
+			}
+
+			@Test
+			void shouldDoDownload() {
+				downloader.startDownload();
+
+				verify(downloader).doDownload();
+			}
+
+			@SneakyThrows
+			@Test
+			void shouldCloseOutputStream() {
+				downloader.startDownload();
+
+				verify(outputStream).close();
+			}
+
+			@Test
+			void shouldSendChunks() {
+				downloader.startDownload();
+
+				verify(downloader).sendChunks();
+			}
+		}
+
+		@Nested
+		class OnException {
+
+			private final TechnicalException exception = new TechnicalException("error");
+
+			@BeforeEach
+			void init() {
+				doThrow(exception).when(downloader).doDownload();
+			}
+
+			@Test
+			void shouldSetError() {
+				downloader.startDownload();
+
+				assertThat(getDownloadError()).isInstanceOf(TechnicalException.class).hasCause(exception);
+			}
+
+			@SneakyThrows
+			@Test
+			void shouldCloseOutputStream() {
+				downloader.startDownload();
+
+				verify(outputStream).close();
+			}
 		}
 	}
 
-	@DisplayName("do")
 	@Nested
 	class TestDoDownload {
 
@@ -192,7 +301,6 @@ class GrpcBinaryFileServerDownloaderTest {
 			setOutputStreamField(outputStream);
 		}
 
-		@SneakyThrows
 		@Test
 		void shouldCallDownloadConsumer() {
 			downloader.doDownload();
@@ -200,164 +308,207 @@ class GrpcBinaryFileServerDownloaderTest {
 			verify(downloadConsumer).accept(outputStream);
 		}
 
-		@SneakyThrows
 		@Test
-		void shouldCloseOutputStream() {
+		void shouldDownloadFinishedBeInitiallyFalse() {
+			assertThat(getDownloadFinished()).isFalse();
+		}
+
+		@Test
+		void shouldSetDownloadFinished() {
 			downloader.doDownload();
 
-			verify(outputStream).close();
+			assertThat(getDownloadFinished()).isTrue();
 		}
 	}
 
-	@DisplayName("Send chunks")
 	@Nested
 	class TestSendChunks {
 
-		@SneakyThrows
-		@Test
-		void shouldCallHandleSafety() {
-			doNothing().when(downloader).doSendChunks();
-
-			downloader.sendChunks();
-
-			verify(downloader).handleSafety(any(ExceptionalRunnable.class));
-		}
+		@Nested
+		class OnNoException {
 
-		@SneakyThrows
-		@Test
-		void shouldCallDoDownload() {
-			doNothing().when(downloader).doSendChunks();
+			@SneakyThrows
+			@BeforeEach
+			void init() {
+				doNothing().when(downloader).doSendChunks();
+			}
 
-			downloader.sendChunks();
+			@SneakyThrows
+			@Test
+			void shouldDoSendChunks() {
+				downloader.sendChunks();
 
-			verify(downloader).doSendChunks();
+				verify(downloader).doSendChunks();
+			}
 		}
 
-		@DisplayName("do")
 		@Nested
-		class TestDoSendChunks {
+		class OnException {
 
-			@Mock
-			private PipedInputStream inputStream;
+			private final IOException exception = new IOException();
 			@Captor
-			private ArgumentCaptor<ByteString> byteStringCaptor;
-
-			private final int readBytes = 20;
-			private final byte[] buffer = new byte[readBytes];
-			private final GrpcResponseDummy grpcResponseDummy = new GrpcResponseDummy();
+			private ArgumentCaptor<TechnicalException> argumentCaptor;
 
 			@SneakyThrows
 			@BeforeEach
-			void mock() {
-				doNothing().when(downloader).tryCompleteRequest();
-				when(callObserver.isReady()).thenReturn(true);
-				when(inputStream.read(any())).thenReturn(readBytes, -1);
-				setInputStreamField(inputStream);
-				new Random().nextBytes(buffer);
-				ReflectionTestUtils.setField(downloader, "buffer", buffer);
+			void init() {
+				doThrow(exception).when(downloader).doSendChunks();
+				doNothing().when(downloader).completeRequestWithError(any());
 			}
 
 			@Test
-			void shouldCallChunkBuilder() {
-				doSendChunks();
+			void shouldCompleteRequestWithError() {
+				downloader.sendChunks();
 
-				verify(chunkBuilder).apply(byteStringCaptor.capture());
-				assertThat(byteStringCaptor.getValue().toByteArray()).isEqualTo(buffer);
+				verify(downloader).completeRequestWithError(argumentCaptor.capture());
+				assertThat(argumentCaptor.getValue()).isInstanceOf(TechnicalException.class).hasCause(exception);
 			}
+		}
+	}
 
-			@DisplayName("should send next chunk if callObserver is ready and stream already received data")
-			@Test
-			void shouldCallOnNext() {
-				when(chunkBuilder.apply(any())).thenReturn(grpcResponseDummy);
+	@Nested
+	class TestDoSendChunks {
 
-				doSendChunks();
+		@Nested
+		class OnRequestFinished {
 
-				verify(callObserver).onNext(grpcResponseDummy);
+			@BeforeEach
+			void init() {
+				setRequestFinishedField(true);
 			}
 
-			@DisplayName("should call complete grpc stream if download has finished and stream has no data left")
 			@Test
-			void shouldCallCompleteDownload() {
-				setDownloadFinishedField(true);
-
+			void shouldNotInteractWithCallObserver() {
 				doSendChunks();
 
-				verify(downloader).tryCompleteRequest();
+				verifyNoInteractions(callObserver);
 			}
+		}
 
-			@SneakyThrows
-			private void doSendChunks() {
-				downloader.doSendChunks();
+		@Nested
+		class OnRequestNotFinished {
+
+			@Nested
+			class OnNotReady {
+
+				@BeforeEach
+				void init() {
+					when(callObserver.isReady()).thenReturn(false);
+				}
+
+				@Test
+				void shouldOnlyCallIsReadyOnObserver() {
+					doSendChunks();
+
+					verify(callObserver).isReady();
+					verifyNoMoreInteractions(callObserver);
+				}
 			}
-		}
-	}
 
-	@Nested
-	class TestTryCompleteRequest {
+			@Nested
+			class OnReady {
+
+				@Mock
+				private PipedInputStream inputStream;
+				@Captor
+				private ArgumentCaptor<ByteString> byteStringCaptor;
+
+				private final int readBytes = 20;
+				private final byte[] buffer = new byte[readBytes];
+				private final GrpcResponseDummy grpcResponseDummy = new GrpcResponseDummy();
+
+				@SneakyThrows
+				@BeforeEach
+				void mock() {
+					doNothing().when(downloader).tryCompleteRequest();
+					when(callObserver.isReady()).thenReturn(true);
+					when(inputStream.read(any())).thenReturn(readBytes, -1);
+					setInputStreamField(inputStream);
+					new Random().nextBytes(buffer);
+					ReflectionTestUtils.setField(downloader, "buffer", buffer);
+				}
 
-		@Test
-		void shouldCallShouldCompleteRequest() {
-			downloader.tryCompleteRequest();
+				@Test
+				void shouldCallChunkBuilder() {
+					doSendChunks();
 
-			verify(downloader).shouldCompleteRequest();
-		}
+					verify(chunkBuilder).apply(byteStringCaptor.capture());
+					assertThat(byteStringCaptor.getValue().toByteArray()).isEqualTo(buffer);
+				}
 
-		@Test
-		void shouldCallCompleteRequest() {
-			doReturn(true).when(downloader).shouldCompleteRequest();
+				@DisplayName("should send next chunk if callObserver is ready and stream already received data")
+				@Test
+				void shouldCallOnNext() {
+					when(chunkBuilder.apply(any())).thenReturn(grpcResponseDummy);
 
-			downloader.tryCompleteRequest();
+					doSendChunks();
 
-			verify(downloader).completeRequest();
-		}
+					verify(callObserver).onNext(grpcResponseDummy);
+				}
 
-		@Test
-		void shouldNotCallCompleteRequest() {
-			doReturn(false).when(downloader).shouldCompleteRequest();
+				@DisplayName("should call complete grpc stream if download has finished and stream has no data left")
+				@Test
+				void shouldTryCompleteRequest() {
+					setDownloadFinishedField(true);
 
-			downloader.tryCompleteRequest();
+					doSendChunks();
 
-			verify(downloader, never()).completeRequest();
+					verify(downloader).tryCompleteRequest();
+				}
+			}
+		}
+
+		@SneakyThrows
+		private void doSendChunks() {
+			downloader.doSendChunks();
 		}
 	}
 
 	@Nested
-	class TestShouldCompleteRequest {
+	class TestTryCompleteRequest {
 
 		@Nested
-		class TestWhenDownloadFinished {
+		class OnError {
+
+			private final TechnicalException exception = new TechnicalException("error");
 
 			@BeforeEach
 			void init() {
-				setDownloadFinishedField(true);
+				setDownloadErrorField(exception);
 			}
 
 			@Test
-			void shouldReturnTrue() {
-				var result = downloader.shouldCompleteRequest();
+			void shouldThrowException() {
+				assertThatThrownBy(downloader::tryCompleteRequest).isSameAs(exception);
+			}
+		}
+
+		@Nested
+		class OnDownloadFinished {
 
-				assertThat(result).isTrue();
+			@BeforeEach
+			void init() {
+				setDownloadFinishedField(true);
+				doNothing().when(downloader).completeRequestNormally();
 			}
 
 			@Test
-			void shouldReturnFalseIfRequestFinished() {
-				setRequestFinishedField(true);
-
-				var result = downloader.shouldCompleteRequest();
+			void shouldNotCompleteRequestWithError() {
+				downloader.tryCompleteRequest();
 
-				assertThat(result).isFalse();
+				verify(downloader, never()).completeRequestWithError(any());
 			}
 
 			@Test
-			void shouldUpdateRequestFinished() {
-				downloader.shouldCompleteRequest();
+			void shouldCompleteRequestNormally() {
+				downloader.tryCompleteRequest();
 
-				assertThat(getRequestFinished()).isTrue();
+				verify(downloader).completeRequestNormally();
 			}
 		}
 
 		@Nested
-		class TestWhenDownloadRunning {
+		class OnDownloadNotFinished {
 
 			@BeforeEach
 			void init() {
@@ -365,116 +516,83 @@ class GrpcBinaryFileServerDownloaderTest {
 			}
 
 			@Test
-			void shouldReturnFalse() {
-				var result = downloader.shouldCompleteRequest();
+			void shouldNotCompleteRequestNormally() {
+				downloader.tryCompleteRequest();
 
-				assertThat(result).isFalse();
+				verify(downloader, never()).completeRequestNormally();
 			}
 
 			@Test
-			void shouldNotUpdateRequestFinished() {
-				downloader.shouldCompleteRequest();
+			void shouldNotCompleteRequestWithError() {
+				downloader.tryCompleteRequest();
 
-				assertThat(getRequestFinished()).isFalse();
+				verify(downloader, never()).completeRequestWithError(any());
 			}
 		}
 	}
 
 	@Nested
-	class TestCompleteRequest {
-
-		@Mock
-		private PipedInputStream inputStream;
+	class TestCompleteRequestNormally {
 
 		@BeforeEach
-		void mock() {
-			setRequestFinishedField(false);
-			setDownloadFinishedField(true);
-			setInputStreamField(inputStream);
+		void init() {
+			doNothing().when(downloader).closeInputStream();
 		}
 
-		@SneakyThrows
 		@Test
-		void shouldCallCloseInputStream() {
-			downloader.completeRequest();
+		void shouldSetRequestFinished() {
+			assertThat(getRequestFinished()).isFalse();
 
-			verify(downloader).closeInputStream();
+			downloader.completeRequestNormally();
+
+			assertThat(getRequestFinished()).isTrue();
 		}
 
 		@Test
-		void shouldCallOnCompleted() {
-			downloader.completeRequest();
+		void shouldCloseInputStream() {
+			downloader.completeRequestNormally();
 
-			verify(callObserver).onCompleted();
+			verify(downloader).closeInputStream();
 		}
 
-		@SneakyThrows
-		private boolean getRequestFinished() {
-			return ReflectionTestUtils.getField(downloader, "requestFinished", AtomicBoolean.class).get();
+		@Test
+		void shouldNotifyObserver() {
+			downloader.completeRequestNormally();
+
+			verify(callObserver).onCompleted();
 		}
 	}
 
-	@DisplayName("Handle safety")
 	@Nested
-	class TestHandleSafety {
+	class TestCompleteRequestWithError {
 
-		@DisplayName("on exception")
-		@Nested
-		class TestOnException {
+		private final TechnicalException error = new TechnicalException("error");
 
-			@Mock
-			private PipedOutputStream outputStream;
-			@Mock
-			private PipedInputStream inputStream;
-
-			private final IOException exception = new IOException();
-
-			@SneakyThrows
-			@BeforeEach
-			void mock() {
-				setInputStreamField(inputStream);
-				setOutputStreamField(outputStream);
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldThrowTechnicalException() {
-				assertThatThrownBy(this::handleSafety).isInstanceOf(TechnicalException.class).extracting(Throwable::getCause).isEqualTo(exception);
-			}
-
-			@SneakyThrows
-			@Test
-			void shouldCloseOutputStream() {
-				try {
-					handleSafety();
-				} catch (Exception e) {
-					// do nothing
-				}
+		@BeforeEach
+		void init() {
+			doNothing().when(downloader).closeInputStream();
+		}
 
-				verify(outputStream).close();
-			}
+		@Test
+		void shouldSetRequestFinished() {
+			assertThat(getRequestFinished()).isFalse();
 
-			@SneakyThrows
-			@Test
-			void shouldCloseInputStream() {
-				try {
-					handleSafety();
-				} catch (Exception e) {
-					// do nothing
-				}
+			catchException(() -> downloader.completeRequestWithError(error));
 
-				verify(inputStream).close();
-			}
+			assertThat(getRequestFinished()).isTrue();
+		}
 
-			private void handleSafety() {
-				downloader.handleSafety(this::dummyMethodThrowingException);
-			}
+		@Test
+		void shouldCloseInputStream() {
+			catchException(() -> downloader.completeRequestWithError(error));
 
-			private void dummyMethodThrowingException() throws IOException {
-				throw exception;
-			}
+			verify(downloader).closeInputStream();
 		}
 
+		@Test
+		void shouldThrowException() {
+			assertThatThrownBy(() -> downloader.completeRequestWithError(error)).isSameAs(error);
+		}
 	}
 
 	private void setOutputStreamField(OutputStream outputStream) {
@@ -485,10 +603,6 @@ class GrpcBinaryFileServerDownloaderTest {
 		ReflectionTestUtils.setField(downloader, "inputStream", inputStream);
 	}
 
-	private void setDownloadFinishedField(boolean downloadFinished) {
-		ReflectionTestUtils.setField(downloader, "downloadFinished", new AtomicBoolean(downloadFinished));
-	}
-
 	private void setRequestFinishedField(boolean requestFinished) {
 		ReflectionTestUtils.setField(downloader, "requestFinished", new AtomicBoolean(requestFinished));
 	}
@@ -497,6 +611,22 @@ class GrpcBinaryFileServerDownloaderTest {
 		return ReflectionTestUtils.getField(downloader, "requestFinished", AtomicBoolean.class).get();
 	}
 
-	static class GrpcResponseDummy {
+	private void setDownloadErrorField(TechnicalException error) {
+		ReflectionTestUtils.setField(downloader, "downloadError", new AtomicReference<>(error));
+	}
+
+	private TechnicalException getDownloadError() {
+		return (TechnicalException) ReflectionTestUtils.getField(downloader, "downloadError", AtomicReference.class).get();
+	}
+
+	private void setDownloadFinishedField(boolean downloadFinished) {
+		ReflectionTestUtils.setField(downloader, "downloadFinished", new AtomicBoolean(downloadFinished));
+	}
+
+	private boolean getDownloadFinished() {
+		return ReflectionTestUtils.getField(downloader, "downloadFinished", AtomicBoolean.class).get();
+	}
+
+	private static class GrpcResponseDummy {
 	}
 }
\ No newline at end of file