Repository: iterative/dvc

Issue:
`dvc exp run` prints exception message twice
Here's the repro:
```console
cd "$(mktemp -d)"
git init && dvc init
echo "foo: 1" > params.yaml
git add -A
git commit -m "initial"
dvc exp init python train.py
dvc exp run
```

This outputs:
```console
ERROR: failed to reproduce 'dvc.yaml': [Errno 2] No such file or directory: '/private/var/folders/xh/trg29z296h70n109kwfk6g800000gn/T/tmp.dUgMYVQs/data'
ERROR: failed to reproduce 'dvc.yaml': [Errno 2] No such file or directory: '/private/var/folders/xh/trg29z296h70n109kwfk6g800000gn/T/tmp.dUgMYVQs/data
```


Discussion:
This is happening because of:

https://github.com/iterative/dvc/blob/822b501069f4553b76534aadf0b2266fd5c26c24/dvc/repo/experiments/executor/base.py#L563-L565

@pmrowla, do we need to log errors here if we want to raise it again anyway?
We normally need to log them here because they do not get re-raised. The exceptions happen inside multiprocessing child process contexts, and an exception is returned w/ the multiprocessing futures, but the exceptions are not re-raised at that point.
https://github.com/iterative/dvc/blob/47d10c4a8b9bdc18dec17a9380f0593e43383c4c/dvc/repo/experiments/executor/manager/base.py#L215

I think the issue here is just a bug in the workspace specific handling after the executor refactors. For workspace runs, we should be passing `log_errors=False` here, since we can just directly pass the exceptions back to the caller (since workspace runs don't happen inside the multiprocessing context)

https://github.com/iterative/dvc/blob/47d10c4a8b9bdc18dec17a9380f0593e43383c4c/dvc/repo/experiments/executor/manager/local.py#L86

Resolving patch:
diff --git a/dvc/repo/experiments/executor/manager/local.py b/dvc/repo/experiments/executor/manager/local.py
--- a/dvc/repo/experiments/executor/manager/local.py
+++ b/dvc/repo/experiments/executor/manager/local.py
@@ -88,6 +88,7 @@ def exec_queue(
                 rev=rev,
                 infofile=infofile,
                 log_level=logger.getEffectiveLevel(),
+                log_errors=not isinstance(executor, WorkspaceExecutor),
             )
             if not exec_result.exp_hash:
                 raise DvcException(


Test patch:
diff --git a/tests/func/experiments/test_experiments.py b/tests/func/experiments/test_experiments.py
--- a/tests/func/experiments/test_experiments.py
+++ b/tests/func/experiments/test_experiments.py
@@ -10,6 +10,7 @@
 from dvc.exceptions import DvcException
 from dvc.repo.experiments.utils import exp_refs_by_rev
 from dvc.scm import resolve_rev
+from dvc.stage.exceptions import StageFileDoesNotExistError
 from dvc.utils.serialize import PythonFileCorruptedError
 from tests.func.test_repro_multistage import COPY_SCRIPT
 
@@ -704,3 +705,15 @@ def test_experiment_name_invalid(tmp_dir, scm, dvc, exp_stage, mocker):
             params=["foo=3"],
         )
     new_mock.assert_not_called()
+
+
+def test_experiments_workspace_not_log_exception(caplog, dvc, scm):
+    """Experiments run in workspace should not log exception.
+
+    Instead it should just leave it to be handled in the main entrypoints.
+    """
+    with caplog.at_level(logging.ERROR):
+        with pytest.raises(StageFileDoesNotExistError):
+            dvc.experiments.run()
+
+    assert not caplog.text