diff --git a/doc/source/reference/kwant.builder.rst b/doc/source/reference/kwant.builder.rst
index c1aa98ebbac9d50ce41d2267d6e2ce9992fa3b9a..5acfab8552ff607f6ef74cef46a169aecb29aee4 100644
--- a/doc/source/reference/kwant.builder.rst
+++ b/doc/source/reference/kwant.builder.rst
@@ -16,6 +16,13 @@ Types
    SelfEnergyLead
    ModesLead
 
+Functions
+---------
+.. autosummary::
+   :toctree: generated/
+
+   format_hopping
+
 Abstract base classes
 ---------------------
 .. autosummary::
@@ -24,3 +31,10 @@ Abstract base classes
    SiteFamily
    Symmetry
    Lead
+
+Exceptions
+----------
+.. autosummary::
+   :toctree: generated/
+
+   BuilderKeyError
diff --git a/kwant/builder.py b/kwant/builder.py
index 44425ca05103ccebe11d643817e8045957d1c478..c209f64d4f79862a6aa3b6c609f16b0a36cd70f7 100644
--- a/kwant/builder.py
+++ b/kwant/builder.py
@@ -9,7 +9,8 @@
 from __future__ import division
 
 __all__ = ['Builder', 'Site', 'SiteFamily', 'SimpleSiteFamily', 'Symmetry',
-           'HoppingKind', 'Lead', 'BuilderLead', 'SelfEnergyLead', 'ModesLead']
+           'HoppingKind', 'Lead', 'BuilderLead', 'SelfEnergyLead', 'ModesLead',
+           'BuilderKeyError', 'format_hopping']
 
 import abc
 import sys
@@ -20,6 +21,35 @@ import tinyarray as ta
 import numpy as np
 from . import system, graph, physics
 
+
+class BuilderKeyError(KeyError):
+    """Builder key (site or hopping) not found.
+
+    This class exists to make KeyError exceptions more readable for hoppings.
+    """
+    def __str__(self):
+        try:
+            arg, = self.args
+            return str(arg if isinstance(arg, Site) else format_hopping(arg))
+        except:
+            # If we end up here, there's a bug somewhere.
+            return "BuilderKeyError expects a single site or hopping."
+
+
+def format_hopping(hopping):
+    """Return a readable string representation of a hopping."""
+    a, b = hopping
+    af = a.family
+    bf = b.family
+    if af == bf:
+        return '<Hopping to {0} from {1}; both of {2}>'.format(
+            a.tag, b.tag, af.name if af.name else af)
+    else:
+        return '<Hopping to {0} of {1}, from {2} of {3}>'.format(
+            a.tag, af.name if af.name else af,
+            b.tag, bf.name if bf.name else bf)
+
+
 
 ################ Sites and site families
 
@@ -76,8 +106,7 @@ class Site(tuple):
 
     def __str__(self):
         sf = self.family
-        return '<Site {1} of {0}>'.format(str(sf.name if sf.name else sf),
-                                          str(self.tag))
+        return '<Site {0} of {1}>'.format(self.tag, sf.name if sf.name else sf)
 
     @property
     def pos(self):
@@ -352,8 +381,8 @@ class HoppingKind(object):
 
     def __str__(self):
         return '{0}({1}, {2}{3})'.format(
-            self.__class__.__name__, str(tuple(self.delta)),
-            str(self.family_a),
+            self.__class__.__name__, tuple(self.delta),
+            self.family_a,
             ', ' + str(self.family_b) if self.family_a != self.family_b else '')
 
 
@@ -751,19 +780,19 @@ class Builder(object):
         try:
             return self.H[site][1]
         except KeyError:
-            raise KeyError(site)
+            raise BuilderKeyError(site)
 
     def _get_hopping(self, hopping):
         sym = self.symmetry
         try:
             a, b = hopping
         except:
-            raise KeyError(hopping)
+            raise BuilderKeyError(hopping)
         try:
             a, b = sym.to_fd(a, b)
             value = self._get_edge(a, b)
-        except ValueError:
-            raise KeyError(hopping)
+        except (KeyError, ValueError):
+            raise BuilderKeyError(hopping)
         if value is Other:
             if not sym.in_fd(b):
                 b, a = sym.to_fd(b, a)
@@ -783,7 +812,7 @@ class Builder(object):
         elif isinstance(key, tuple):
             return self._get_hopping(key)
         else:
-            raise KeyError(key)
+            raise BuilderKeyError(key)
 
     def __contains__(self, key):
         """Tell whether the system contains a site or hopping."""
@@ -796,7 +825,7 @@ class Builder(object):
             hvhv = self.H.get(a, ())
             return b in islice(hvhv, 2, None, 2)
         else:
-            raise KeyError(key)
+            raise BuilderKeyError(key)
 
     def _set_site(self, site, value):
         """Set a single site."""
@@ -813,9 +842,9 @@ class Builder(object):
         try:
             a, b = hopping
         except:
-            raise KeyError(hopping)
+            raise BuilderKeyError(hopping)
         if a == b:
-            raise KeyError(hopping)
+            raise BuilderKeyError(hopping)
         if isinstance(value, HermConjOfFunc):
             a, b = b, a
             value = value.function
@@ -835,12 +864,12 @@ class Builder(object):
             else:
                 b2, a2 = sym.to_fd(b, a)
                 if b2 not in self.H:
-                    raise KeyError()
+                    raise BuilderKeyError()
                 assert not sym.in_fd(a2)
                 self._set_edge(a, b, value)      # Might fail.
                 self._set_edge(b2, a2, Other)    # Will work.
         except KeyError:
-            raise KeyError(hopping)
+            raise BuilderKeyError(hopping)
 
     def __setitem__(self, key, value):
         """Set a single site/hopping or a bunch of them."""
@@ -866,9 +895,12 @@ class Builder(object):
                     assert not self.symmetry.in_fd(neighbor)
                     a, b = tfd(neighbor, site)
                     self._del_edge(a, b)
-        except ValueError:
-            raise KeyError(site)
-        del self.H[site]
+        except (KeyError, ValueError):
+            raise BuilderKeyError(site)
+        try:
+            del self.H[site]
+        except KeyError:
+            raise BuilderKeyError(site)
 
     def _del_hopping(self, hopping):
         """Delete a single hopping."""
@@ -877,7 +909,7 @@ class Builder(object):
         try:
             a, b = hopping
         except:
-            raise KeyError(hopping)
+            raise BuilderKeyError(hopping)
         try:
             a, b = sym.to_fd(a, b)
             if sym.in_fd(b):
@@ -888,8 +920,8 @@ class Builder(object):
                 b, a = sym.to_fd(b, a)
                 assert not sym.in_fd(a)
                 self._del_edge(b, a)
-        except ValueError:
-            raise KeyError(hopping)
+        except (KeyError, ValueError):
+            raise BuilderKeyError(hopping)
 
     def __delitem__(self, key):
         """Delete a single site/hopping or bunch of them."""
@@ -955,7 +987,7 @@ class Builder(object):
             for head, value in edges(hvhv):
                 if value is Other:
                     continue
-                yield (tail, head)
+                yield tail, head
 
     def hopping_value_pairs(self):
         """Return an iterator over all (hopping, value) pairs."""
@@ -1030,7 +1062,7 @@ class Builder(object):
             raise ValueError('Only builders with a 1D symmetry are allowed.')
         for hopping in lead_builder.hoppings():
             if not -1 <= sym.which(hopping[1])[0] <= 1:
-                msg = ('Hopping {0} connects non-neighboring lead unit cells. '
+                msg = ('{0} connects non-neighboring lead unit cells. '
                        'Only nearest-cell hoppings are allowed '
                        '(consider increasing the lead period).')
                 raise ValueError(msg.format(hopping))