Use the proper error message with caused exceptions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 273855ecd..f16bf8108 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -40,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 
 - Fixed redirecting of stderr in Progress
 - Fixed broken expanded tuple of one https://github.com/willmcgugan/rich/issues/445
+- Fixed traceback message with `from` exceptions
 - Fixed justify argument not working in console.log https://github.com/willmcgugan/rich/issues/460
 
 ## [9.2.0] - 2020-11-08
diff --git a/rich/traceback.py b/rich/traceback.py
index 1d9b06590..3d13becbe 100644
--- a/rich/traceback.py
+++ b/rich/traceback.py
@@ -124,6 +124,7 @@ class Stack:
     exc_type: str
     exc_value: str
     syntax_error: Optional[_SyntaxError] = None
+    is_cause: bool = False
     frames: List[Frame] = field(default_factory=list)
 
 
@@ -259,8 +260,13 @@ def extract(
         """
 
         stacks: List[Stack] = []
+        is_cause = False
         while True:
-            stack = Stack(exc_type=str(exc_type.__name__), exc_value=str(exc_value))
+            stack = Stack(
+                exc_type=str(exc_type.__name__),
+                exc_value=str(exc_value),
+                is_cause=is_cause,
+            )
 
             if isinstance(exc_value, SyntaxError):
                 stack.syntax_error = _SyntaxError(
@@ -294,12 +300,22 @@ def extract(
                 )
                 append(frame)
 
+            cause = getattr(exc_value, "__cause__", None)
+            if cause and cause.__traceback__:
+                exc_type = cause.__class__
+                exc_value = cause
+                traceback = cause.__traceback__
+                if traceback:
+                    is_cause = True
+                    continue
+
             cause = exc_value.__context__
             if cause and cause.__traceback__:
                 exc_type = cause.__class__
                 exc_value = cause
                 traceback = cause.__traceback__
                 if traceback:
+                    is_cause = False
                     continue
             # No cover, code is reached but coverage doesn't recognize it.
             break  # pragma: no cover
@@ -373,9 +389,14 @@ def __rich_console__(
                 )
 
             if not last:
-                yield Text.from_markup(
-                    "\n[i]During handling of the above exception, another exception occurred:\n",
-                )
+                if stack.is_cause:
+                    yield Text.from_markup(
+                        "\n[i]The above exception was the direct cause of the following exception:\n",
+                    )
+                else:
+                    yield Text.from_markup(
+                        "\n[i]During handling of the above exception, another exception occurred:\n",
+                    )
 
     @render_group()
     def _render_syntax_error(self, syntax_error: _SyntaxError) -> RenderResult:
diff --git a/tests/test_traceback.py b/tests/test_traceback.py
index 449e5f49e..628e05c9b 100644
--- a/tests/test_traceback.py
+++ b/tests/test_traceback.py
@@ -94,7 +94,7 @@ def test_syntax_error():
 
 def test_nested_exception():
     console = Console(width=100, file=io.StringIO())
-    value_error_message = "ValueError because of ZeroDivisionEerror"
+    value_error_message = "ValueError because of ZeroDivisionError"
 
     try:
         try:
@@ -112,7 +112,35 @@ def test_nested_exception():
         "During handling of the above exception",
     ]
 
-    assert [msg in exception_text for msg in text_should_contain]
+    for msg in text_should_contain:
+        assert msg in exception_text
+
+    # ZeroDivisionError should come before ValueError
+    assert exception_text.find("ZeroDivisionError") < exception_text.find("ValueError")
+
+
+def test_caused_exception():
+    console = Console(width=100, file=io.StringIO())
+    value_error_message = "ValueError caused by ZeroDivisionError"
+
+    try:
+        try:
+            1 / 0
+        except ZeroDivisionError as e:
+            raise ValueError(value_error_message) from e
+    except Exception:
+        console.print_exception()
+    exception_text = console.file.getvalue()
+
+    text_should_contain = [
+        value_error_message,
+        "ZeroDivisionError",
+        "ValueError",
+        "The above exception was the direct cause",
+    ]
+
+    for msg in text_should_contain:
+        assert msg in exception_text
 
     # ZeroDivisionError should come before ValueError
     assert exception_text.find("ZeroDivisionError") < exception_text.find("ValueError")

## Type of changes

- [x] Bug fix
- [ ] New feature
- [ ] Documentation / docstrings
- [x] Tests
- [ ] Other

## Checklist

- [x] I've run the latest [black](https://github.com/psf/black) with default args on new code.
- [x] I've updated CHANGELOG.md and CONTRIBUTORS.md where appropriate.
- [x] I've added tests for new code.
- [x] I accept that @willmcgugan may be pedantic in the code review.

## Description

When an exception is raised using "from", the chained exception goes into
__cause__, which results in a different message using Python's native traceback
handler. This patch properly replicates that error message.
