diff --git a/src/test/perl/PostgreSQL/Test/Cluster.pm b/src/test/perl/PostgreSQL/Test/Cluster.pm
index 1c11750ac1d..c1ba9de9723 100644
--- a/src/test/perl/PostgreSQL/Test/Cluster.pm
+++ b/src/test/perl/PostgreSQL/Test/Cluster.pm
@@ -2143,11 +2143,9 @@ sub psql
 		'--dbname' => $psql_connstr,
 		'--file' => '-');
 
-	# If the caller wants an array and hasn't passed stdout/stderr
-	# references, allocate temporary ones to capture them so we
-	# can return them. Otherwise we won't redirect them at all.
-	if (wantarray)
-	{
+	# Always define undef stdout and stderr. This is needed for wantarray case,
+	# and stderr is used with params{on_error_die}, even if not undefined
+
 	if (!defined($stdout))
 	{
 		my $temp_stdout = "";
@@ -2158,7 +2156,15 @@ sub psql
 		my $temp_stderr = "";
 		$stderr = \$temp_stderr;
 	}
-	}
+
+	my $fh_stdout = File::Temp->new();
+	my $fh_stderr = File::Temp->new();
+
+	open(my $orig_stdout, '>&', \*STDOUT) or die $!;
+	open(my $orig_stderr, '>&', \*STDERR) or die $!;
+
+	tie *fh_stdout, "PostgreSQL::Test::SimpleTee", $orig_stdout;
+	tie *fh_stderr, "PostgreSQL::Test::SimpleTee", $orig_stderr;
 
 	$params{on_error_stop} = 1 unless defined $params{on_error_stop};
 	$params{on_error_die} = 0 unless defined $params{on_error_die};
@@ -2191,8 +2197,8 @@ sub psql
 		local $@;
 		eval {
 			my @ipcrun_opts = (\@psql_params, '<' => \$sql);
-			push @ipcrun_opts, '>' => $stdout if defined $stdout;
-			push @ipcrun_opts, '2>' => $stderr if defined $stderr;
+			push @ipcrun_opts, '>' => $fh_stdout;
+			push @ipcrun_opts, '2>' => $fh_stderr;
 			push @ipcrun_opts, $timeout if defined $timeout;
 
 			IPC::Run::run @ipcrun_opts;
@@ -2225,6 +2231,9 @@ sub psql
 		}
 	};
 
+	$$stdout = PostgreSQL::Test::Utils::slurp_file($fh_stdout);
+	$$stderr = PostgreSQL::Test::Utils::slurp_file($fh_stderr);
+
 	if (defined $$stdout)
 	{
 		chomp $$stdout;
