Repository: iterative/dvc

Issue:
NoneType error in dvc exp list -A
The error occurs sometimes when `dvc exp list -A` is run. This is the verbose output:

```
2022-11-12 11:32:17,096 ERROR: unexpected error - 'NoneType' object is not subscriptable
------------------------------------------------------------
Traceback (most recent call last):
  File "dvc/cli/__init__.py", line 185, in main
  File "dvc/cli/command.py", line 22, in do_run
  File "dvc/commands/experiments/ls.py", line 27, in run
TypeError: 'NoneType' object is not subscriptable
------------------------------------------------------------
```

Seems like it is caused by this line:
https://github.com/iterative/dvc/blob/014a38fae29dc0b9fbba88e07afd45f278dcdeb6/dvc/commands/experiments/ls.py#L25-L29

From reading the code, if `baseline` is not in `ref_heads`  in line 27, we should move gracefully to line 28, but this will never happen due to above error, since `None` is subscripted and that doesn't work.


Discussion:

Resolving patch:
diff --git a/dvc/commands/experiments/ls.py b/dvc/commands/experiments/ls.py
--- a/dvc/commands/experiments/ls.py
+++ b/dvc/commands/experiments/ls.py
@@ -18,15 +18,13 @@ def run(self):
         )
         tags = self.repo.scm.describe(exps)
         remained = {baseline for baseline, tag in tags.items() if tag is None}
-        base = "refs/heads/"
+        base = "refs/heads"
         ref_heads = self.repo.scm.describe(remained, base=base)
 
         for baseline in exps:
-            name = (
-                tags[baseline]
-                or ref_heads[baseline][len(base) :]
-                or baseline[:7]
-            )
+            name = baseline[:7]
+            if tags[baseline] or ref_heads[baseline]:
+                name = tags[baseline] or ref_heads[baseline][len(base) + 1 :]
             if not name_only:
                 print(f"{name}:")
             for exp_name in exps[baseline]:


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
@@ -417,7 +417,7 @@ def test_packed_args_exists(tmp_dir, scm, dvc, exp_stage, caplog):
         assert "Temporary DVC file" in caplog.text
 
 
-def test_list(tmp_dir, scm, dvc, exp_stage):
+def _prepare_experiments(tmp_dir, scm, dvc, exp_stage):
     baseline_a = scm.get_rev()
     results = dvc.experiments.run(exp_stage.addressing, params=["foo=2"])
     exp_a = first(results)
@@ -433,6 +433,18 @@ def test_list(tmp_dir, scm, dvc, exp_stage):
     exp_c = first(results)
     ref_info_c = first(exp_refs_by_rev(scm, exp_c))
 
+    return baseline_a, baseline_c, ref_info_a, ref_info_b, ref_info_c
+
+
+def test_list(tmp_dir, scm, dvc, exp_stage):
+    (
+        baseline_a,
+        baseline_c,
+        ref_info_a,
+        ref_info_b,
+        ref_info_c,
+    ) = _prepare_experiments(tmp_dir, scm, dvc, exp_stage)
+
     assert dvc.experiments.ls() == {baseline_c: [ref_info_c.name]}
 
     exp_list = dvc.experiments.ls(rev=ref_info_a.baseline_sha)
@@ -447,6 +459,25 @@ def test_list(tmp_dir, scm, dvc, exp_stage):
     }
 
 
+def test_list_cli(tmp_dir, scm, dvc, capsys, exp_stage):
+    from dvc.cli import main
+
+    baseline_a, _, ref_info_a, ref_info_b, ref_info_c = _prepare_experiments(
+        tmp_dir, scm, dvc, exp_stage
+    )
+
+    # Make sure that we prioritize the current branch name
+    scm.checkout("branch", True)
+
+    capsys.readouterr()
+    assert main(["exp", "list", "-A"]) == 0
+    cap = capsys.readouterr()
+    assert set(cap.out.split()) == set(
+        ["branch:", baseline_a[:7] + ":"]
+        + [ref_info_a.name, ref_info_b.name, ref_info_c.name]
+    )
+
+
 def test_subdir(tmp_dir, scm, dvc, workspace):
     subdir = tmp_dir / "dir"
     subdir.gen("copy.py", COPY_SCRIPT)