diff --git a/dictknife/jsonknife/_wrapped_exception.py b/dictknife/jsonknife/_wrapped_exception.py
new file mode 100644
index 0000000..560c087
--- /dev/null
+++ b/dictknife/jsonknife/_wrapped_exception.py
@@ -0,0 +1,40 @@
+from dictknife.langhelpers import reify
+
+
+class WrappedExceptionFactory:
+    def __init__(self, *, prefix="Wrapped", mixin_class=None):
+        self.prefix = prefix
+        self.mixin_class = mixin_class or WrappedMixin
+        self.classes = {}
+
+    def __call__(self, e: Exception, *, where: str) -> Exception:
+        if isinstance(e, self.mixin_class):
+            e.stack.append(where)
+            return e
+
+        cls = self.classes.get(e.__class__)
+        if cls is None:
+            cls = type(
+                f"{self.prefix}{e.__class__.__name__}", (WrappedMixin, e.__class__), {}
+            )
+            self.classes[e.__class__] = cls
+
+        # sync
+        exc = cls.__new__(cls).with_traceback(e.__traceback__)
+        exc.__dict__.update(e.__dict__)
+        if hasattr(e, "args"):
+            exc.args = e.args
+        return self(exc, where=where)
+
+
+class WrappedMixin:
+    @reify
+    def stack(self):
+        # This is just a list, but appended from inner to outer, on except-clause, usually.
+        return []
+
+    def __str__(self):
+        return f"{super().__str__()} (where={tuple(reversed(self.stack))})"
+
+
+wrap_exception = WrappedExceptionFactory(prefix="Ex")
diff --git a/dictknife/jsonknife/accessor.py b/dictknife/jsonknife/accessor.py
index 92d09a3..2b602a4 100644
--- a/dictknife/jsonknife/accessor.py
+++ b/dictknife/jsonknife/accessor.py
@@ -4,6 +4,7 @@ from dictknife import Accessor
 from dictknife import operators
 from dictknife.langhelpers import as_jsonpointer, as_path_node
 
+from ._wrapped_exception import wrap_exception
 
 logger = logging.getLogger(__name__)
 
@@ -132,9 +133,10 @@ def maybe_remove_by_json_pointer(doc, query, *, accessor=Accessor()):
 
 
 class StackedAccessor:
-    def __init__(self, resolver, accessor=Accessor()):
+    def __init__(self, resolver, *, accessor=Accessor(), wrap_exception=wrap_exception):
         self.stack = [resolver]
         self.accessor = accessor
+        self.wrap_exception = wrap_exception
 
     @property
     def resolver(self):
@@ -149,10 +151,7 @@ class StackedAccessor:
             where = None
             if len(self.stack) > 1:
                 where = self.stack[-2].name
-            exc = e.__class__("{} (where={})".format(e, where)).with_traceback(
-                e.__traceback__
-            )
-            exc.__dict__.update(e.__dict__)  # sync
+            exc = self.wrap_exception(e, where=where)
             raise exc from None
 
     def _access(self, subresolver, pointer):
diff --git a/dictknife/jsonknife/resolver.py b/dictknife/jsonknife/resolver.py
index e134543..85d6309 100644
--- a/dictknife/jsonknife/resolver.py
+++ b/dictknife/jsonknife/resolver.py
@@ -9,7 +9,7 @@ from dictknife.langhelpers import reify, pairrsplit
 
 from .accessor import AccessingMixin
 from .relpath import normpath
-
+from ._wrapped_exception import wrap_exception
 
 logger = logging.getLogger("jsonknife.resolver")
 
@@ -41,7 +41,8 @@ class ExternalFileResolver(AccessingMixin):
         doc=None,
         rawfilename=None,
         onload=None,
-        format=None
+        format=None,
+        wrap_exception=wrap_exception
     ):
         self.rawfilename = rawfilename or filename
         self.filename = os.path.normpath(os.path.abspath(str(filename)))
@@ -50,6 +51,7 @@ class ExternalFileResolver(AccessingMixin):
         self.history = history or [ROOT]
         self.onload = onload
         self.format = format
+        self.wrap_exception = wrap_exception
         if doc is not None:
             self.doc = doc
             if self.onload is not None:
@@ -77,13 +79,7 @@ class ExternalFileResolver(AccessingMixin):
                 self.onload(self.doc, self)
             return self.doc
         except Exception as e:
-            try:
-                exc = e.__class__("{} (where={})".format(e, self.name)).with_traceback(
-                    e.__traceback__
-                )
-                exc.__dict__.update(e.__dict__)  # sync
-            except Exception:
-                raise e from None
+            exc = self.wrap_exception(e, where=self.name)
             raise exc from None
 
     def new(self, filename, doc=None, rawfilename=None, format=None):
@@ -99,6 +95,7 @@ class ExternalFileResolver(AccessingMixin):
             rawfilename=rawfilename,
             onload=self.onload,
             format=format,
+            wrap_exception=self.wrap_exception,
         )
 
     def resolve_pathset(self, query):  # todo: refactoring

jsonknife accessor, need WrappedError
hmm.

```
FileNotFoundError("[Errno 2] No such file or directory: 'cwd/example_linter/right.yaml' (where=cwd/example_linter/right.yaml) (where=cwd/example_linter/ng-pair.yaml)")
```

diff --git a/dictknife/tests/jsonknife/test_wrapped_exception.py b/dictknife/tests/jsonknife/test_wrapped_exception.py
new file mode 100644
index 0000000..a650581
--- /dev/null
+++ b/dictknife/tests/jsonknife/test_wrapped_exception.py
@@ -0,0 +1,55 @@
+import unittest
+
+
+class Tests(unittest.TestCase):
+    def _makeOne(self, *args, **kwargs):
+        from dictknife.jsonknife._wrapped_exception import WrappedExceptionFactory
+
+        return WrappedExceptionFactory(*args, **kwargs)
+
+    def test_it(self):
+        prefix = "Wrapped"
+        create_class = self._makeOne(prefix=prefix)
+        try:
+            raise KeyError("hmm")
+        except KeyError as e:
+            e2 = create_class(e, where="<toplevel>")
+            self.assertIsInstance(e2, KeyError)
+            self.assertEqual(e2.args, e.args)
+
+            self.assertEqual(e2.__class__.__name__, f"{prefix}KeyError")
+            self.assertListEqual(e2.stack, ["<toplevel>"])
+        else:
+            self.fail("must be raised")
+
+    def test_nested(self):
+        create_class = self._makeOne()
+
+        def f():
+            try:
+                return g()
+            except Exception as e:
+                raise create_class(e, where="f") from None
+
+        def g():
+            try:
+                return h()
+            except Exception as e:
+                raise create_class(e, where="g") from None
+
+        def h():
+            raise MyException("hmm", context="oyoyo")
+
+        class MyException(Exception):
+            def __init__(self, val, *, context):
+                super().__init__(val)
+                self.context = context
+
+        try:
+            f()
+        except Exception as e:
+            self.assertIsInstance(e, MyException)
+            self.assertListEqual(e.stack, ["g", "f"])
+            self.assertEqual(getattr(e, "context", None), "oyoyo")
+        else:
+            self.fail("must be raised")

pip install -e .[command]
parse_log_pytest
pytest
flake8 pytest
3.7
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///croot/attrs_1668696182826/work
certifi @ file:///croot/certifi_1671487769961/work/certifi
-e git+https://github.com/podhmo/dictknife.git@64144c642cd0f291f8f98f4fde2eca50a6661181#egg=dictknife
flake8==5.0.4
flit_core @ file:///opt/conda/conda-bld/flit-core_1644941570762/work/source/flit_core
importlib-metadata==4.2.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
magicalimport==0.9.1
mccabe==0.7.0
packaging @ file:///croot/packaging_1671697413597/work
pluggy @ file:///tmp/build/80754af9/pluggy_1648042572264/work
prestring==0.9.0
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pycodestyle==2.9.1
pyflakes==2.5.0
pytest==7.1.2
PyYAML==6.0.1
tomli @ file:///opt/conda/conda-bld/tomli_1657175507142/work
typing_extensions @ file:///croot/typing_extensions_1669924550328/work
zipp @ file:///croot/zipp_1672387121353/work

name: dictknife
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=22.1.0=py37h06a4308_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - flit-core=3.6.0=pyhd3eb1b0_0
  - importlib_metadata=4.11.3=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=22.0=py37h06a4308_0
  - pip=22.3.1=py37h06a4308_0
  - pluggy=1.0.0=py37h06a4308_1
  - py=1.11.0=pyhd3eb1b0_0
  - pytest=7.1.2=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tomli=2.0.1=py37h06a4308_0
  - typing_extensions=4.4.0=py37h06a4308_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.11.0=py37h06a4308_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - flake8==5.0.4
      - importlib-metadata==4.2.0
      - magicalimport==0.9.1
      - mccabe==0.7.0
      - prestring==0.9.0
      - pycodestyle==2.9.1
      - pyflakes==2.5.0
      - pyyaml==6.0.1
prefix: /opt/conda/envs/dictknife

dictknife/tests/jsonknife/test_wrapped_exception.py::Tests::test_it dictknife/tests/jsonknife/test_wrapped_exception.py::Tests::test_nested
dictknife/jsonknife/resolver.py dictknife/jsonknife/_wrapped_exception.py dictknife/jsonknife/accessor.py
dictknife/jsonknife/resolver.py dictknife/jsonknife/_wrapped_exception.py dictknife/jsonknife/accessor.py