diff --git a/README.md b/README.md
index b671f357..11d350ef 100644
--- a/README.md
+++ b/README.md
@@ -398,7 +398,7 @@ $ make
 $ ./json_unit "*"
 
 ===============================================================================
-All tests passed (3341848 assertions in 28 test cases)
+All tests passed (3341888 assertions in 28 test cases)
 ```
 
 For more information, have a look at the file [.travis.yml](https://github.com/nlohmann/json/blob/master/.travis.yml).
diff --git a/doc/examples/at__object_t_key_type.cpp b/doc/examples/at__object_t_key_type.cpp
index 85de5656..29c24c0c 100644
--- a/doc/examples/at__object_t_key_type.cpp
+++ b/doc/examples/at__object_t_key_type.cpp
@@ -26,8 +26,8 @@ int main()
     {
         object.at("the fast") = "il rapido";
     }
-    catch (std::out_of_range)
+    catch (std::out_of_range& e)
     {
-        std::cout << "out of range" << '\n';
+        std::cout << "out of range: " << e.what() << '\n';
     }
 }
diff --git a/doc/examples/at__object_t_key_type.link b/doc/examples/at__object_t_key_type.link
index 1f495637..beba6a0f 100644
--- a/doc/examples/at__object_t_key_type.link
+++ b/doc/examples/at__object_t_key_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/syN4hQrhPvlUy5AG"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/3ir8W1OZ5KtbRz6r"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__object_t_key_type.output b/doc/examples/at__object_t_key_type.output
index 90ccb1cd..79cff2d7 100644
--- a/doc/examples/at__object_t_key_type.output
+++ b/doc/examples/at__object_t_key_type.output
@@ -1,3 +1,3 @@
 "il brutto"
 {"the bad":"il cattivo","the good":"il buono","the ugly":"il brutto"}
-out of range
+out of range: key 'the fast' not found
diff --git a/doc/examples/at__size_type.cpp b/doc/examples/at__size_type.cpp
index 5195005e..617485bf 100644
--- a/doc/examples/at__size_type.cpp
+++ b/doc/examples/at__size_type.cpp
@@ -21,8 +21,8 @@ int main()
     {
         array.at(5) = "sixth";
     }
-    catch (std::out_of_range)
+    catch (std::out_of_range& e)
     {
-        std::cout << "out of range" << '\n';
+        std::cout << "out of range: " << e.what() << '\n';
     }
 }
diff --git a/doc/examples/at__size_type.link b/doc/examples/at__size_type.link
index 49830f7b..a5ce10e4 100644
--- a/doc/examples/at__size_type.link
+++ b/doc/examples/at__size_type.link
@@ -1 +1 @@
-<a target="_blank" href="http://melpon.org/wandbox/permlink/wKBBW3ORmTHPlgJV"><b>online</b></a>
\ No newline at end of file
+<a target="_blank" href="http://melpon.org/wandbox/permlink/9Ae4DO4HJjULnq5j"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/at__size_type.output b/doc/examples/at__size_type.output
index 93748f7f..d1f68bdb 100644
--- a/doc/examples/at__size_type.output
+++ b/doc/examples/at__size_type.output
@@ -1,3 +1,3 @@
 "third"
 ["first","second","third","fourth"]
-out of range
+out of range: array index 5 is out of range
diff --git a/src/json.hpp b/src/json.hpp
index e11bae2d..ee6b1743 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -2602,9 +2602,10 @@ class basic_json
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array
+    @throw std::domain_error if the JSON value is not an array; example:
+    `"cannot use at() with string"`
     @throw std::out_of_range if the index @a idx is out of range of the array;
-    that is, `idx >= size()`
+    that is, `idx >= size()`; example: `"array index 7 is out of range"`
 
     @complexity Constant.
 
@@ -2618,7 +2619,15 @@ class basic_json
         // at only works for arrays
         if (is_array())
         {
-            return m_value.array->at(idx);
+            try
+            {
+                return m_value.array->at(idx);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+            }
         }
         else
         {
@@ -2636,9 +2645,10 @@ class basic_json
 
     @return const reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array
+    @throw std::domain_error if the JSON value is not an array; example:
+    `"cannot use at() with string"`
     @throw std::out_of_range if the index @a idx is out of range of the array;
-    that is, `idx >= size()`
+    that is, `idx >= size()`; example: `"array index 7 is out of range"`
 
     @complexity Constant.
 
@@ -2652,7 +2662,15 @@ class basic_json
         // at only works for arrays
         if (is_array())
         {
-            return m_value.array->at(idx);
+            try
+            {
+                return m_value.array->at(idx);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+            }
         }
         else
         {
@@ -2670,9 +2688,10 @@ class basic_json
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object
+    @throw std::domain_error if the JSON value is not an object; example:
+    `"cannot use at() with boolean"`
     @throw std::out_of_range if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`
+    that is, `find(key) == end()`; example: `"key "the fast" not found"`
 
     @complexity Logarithmic in the size of the container.
 
@@ -2690,7 +2709,15 @@ class basic_json
         // at only works for objects
         if (is_object())
         {
-            return m_value.object->at(key);
+            try
+            {
+                return m_value.object->at(key);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("key '" + key + "' not found");
+            }
         }
         else
         {
@@ -2708,9 +2735,10 @@ class basic_json
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object
+    @throw std::domain_error if the JSON value is not an object; example:
+    `"cannot use at() with boolean"`
     @throw std::out_of_range if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`
+    that is, `find(key) == end()`; example: `"key "the fast" not found"`
 
     @complexity Logarithmic in the size of the container.
 
@@ -2728,7 +2756,15 @@ class basic_json
         // at only works for objects
         if (is_object())
         {
-            return m_value.object->at(key);
+            try
+            {
+                return m_value.object->at(key);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("key '" + key + "' not found");
+            }
         }
         else
         {
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 119ec5c2..cea396d6 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -2602,9 +2602,10 @@ class basic_json
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array
+    @throw std::domain_error if the JSON value is not an array; example:
+    `"cannot use at() with string"`
     @throw std::out_of_range if the index @a idx is out of range of the array;
-    that is, `idx >= size()`
+    that is, `idx >= size()`; example: `"array index 7 is out of range"`
 
     @complexity Constant.
 
@@ -2618,7 +2619,15 @@ class basic_json
         // at only works for arrays
         if (is_array())
         {
-            return m_value.array->at(idx);
+            try
+            {
+                return m_value.array->at(idx);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+            }
         }
         else
         {
@@ -2636,9 +2645,10 @@ class basic_json
 
     @return const reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array
+    @throw std::domain_error if the JSON value is not an array; example:
+    `"cannot use at() with string"`
     @throw std::out_of_range if the index @a idx is out of range of the array;
-    that is, `idx >= size()`
+    that is, `idx >= size()`; example: `"array index 7 is out of range"`
 
     @complexity Constant.
 
@@ -2652,7 +2662,15 @@ class basic_json
         // at only works for arrays
         if (is_array())
         {
-            return m_value.array->at(idx);
+            try
+            {
+                return m_value.array->at(idx);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+            }
         }
         else
         {
@@ -2670,9 +2688,10 @@ class basic_json
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object
+    @throw std::domain_error if the JSON value is not an object; example:
+    `"cannot use at() with boolean"`
     @throw std::out_of_range if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`
+    that is, `find(key) == end()`; example: `"key "the fast" not found"`
 
     @complexity Logarithmic in the size of the container.
 
@@ -2690,7 +2709,15 @@ class basic_json
         // at only works for objects
         if (is_object())
         {
-            return m_value.object->at(key);
+            try
+            {
+                return m_value.object->at(key);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("key '" + key + "' not found");
+            }
         }
         else
         {
@@ -2708,9 +2735,10 @@ class basic_json
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object
+    @throw std::domain_error if the JSON value is not an object; example:
+    `"cannot use at() with boolean"`
     @throw std::out_of_range if the key @a key is is not stored in the object;
-    that is, `find(key) == end()`
+    that is, `find(key) == end()`; example: `"key "the fast" not found"`
 
     @complexity Logarithmic in the size of the container.
 
@@ -2728,7 +2756,15 @@ class basic_json
         // at only works for objects
         if (is_object())
         {
-            return m_value.object->at(key);
+            try
+            {
+                return m_value.object->at(key);
+            }
+            catch (std::out_of_range& e)
+            {
+                // create better exception explanation
+                throw std::out_of_range("key '" + key + "' not found");
+            }
         }
         else
         {
diff --git a/test/unit.cpp b/test/unit.cpp
index e52128ce..3a89e94f 100644
--- a/test/unit.cpp
+++ b/test/unit.cpp
@@ -2662,6 +2662,16 @@ TEST_CASE("element access")
             {
                 CHECK_THROWS_AS(j.at(7), std::out_of_range);
                 CHECK_THROWS_AS(j_const.at(7), std::out_of_range);
+
+                // exception name
+                try
+                {
+                    j.at(7);
+                }
+                catch (std::out_of_range& e)
+                {
+                    CHECK(std::string(e.what()) == "array index 7 is out of range");
+                }
             }
 
             SECTION("access on non-array type")
@@ -2672,6 +2682,16 @@ TEST_CASE("element access")
                     const json j_nonarray_const(j_nonarray);
                     CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
                     CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonarray.at(0);
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with null");
+                    }
                 }
 
                 SECTION("boolean")
@@ -2680,6 +2700,16 @@ TEST_CASE("element access")
                     const json j_nonarray_const(j_nonarray);
                     CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
                     CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonarray.at(0);
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with boolean");
+                    }
                 }
 
                 SECTION("string")
@@ -2688,6 +2718,16 @@ TEST_CASE("element access")
                     const json j_nonarray_const(j_nonarray);
                     CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
                     CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonarray.at(0);
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with string");
+                    }
                 }
 
                 SECTION("object")
@@ -2696,6 +2736,16 @@ TEST_CASE("element access")
                     const json j_nonarray_const(j_nonarray);
                     CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
                     CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonarray.at(0);
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with object");
+                    }
                 }
 
                 SECTION("number (integer)")
@@ -2704,6 +2754,16 @@ TEST_CASE("element access")
                     const json j_nonarray_const(j_nonarray);
                     CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
                     CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonarray.at(0);
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with number");
+                    }
                 }
 
                 SECTION("number (floating-point)")
@@ -2712,6 +2772,16 @@ TEST_CASE("element access")
                     const json j_nonarray_const(j_nonarray);
                     CHECK_THROWS_AS(j_nonarray.at(0), std::domain_error);
                     CHECK_THROWS_AS(j_nonarray_const.at(0), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonarray.at(0);
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with number");
+                    }
                 }
             }
         }
@@ -3028,6 +3098,16 @@ TEST_CASE("element access")
             {
                 CHECK_THROWS_AS(j.at("foo"), std::out_of_range);
                 CHECK_THROWS_AS(j_const.at("foo"), std::out_of_range);
+
+                // exception name
+                try
+                {
+                    j.at("foo");
+                }
+                catch (std::out_of_range& e)
+                {
+                    CHECK(std::string(e.what()) == "key 'foo' not found");
+                }
             }
 
             SECTION("access on non-object type")
@@ -3038,6 +3118,16 @@ TEST_CASE("element access")
                     const json j_nonobject_const(j_nonobject);
                     CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
                     CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonobject.at("foo");
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with null");
+                    }
                 }
 
                 SECTION("boolean")
@@ -3046,6 +3136,16 @@ TEST_CASE("element access")
                     const json j_nonobject_const(j_nonobject);
                     CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
                     CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonobject.at("foo");
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with boolean");
+                    }
                 }
 
                 SECTION("string")
@@ -3054,6 +3154,16 @@ TEST_CASE("element access")
                     const json j_nonobject_const(j_nonobject);
                     CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
                     CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonobject.at("foo");
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with string");
+                    }
                 }
 
                 SECTION("array")
@@ -3062,6 +3172,16 @@ TEST_CASE("element access")
                     const json j_nonobject_const(j_nonobject);
                     CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
                     CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonobject.at("foo");
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with array");
+                    }
                 }
 
                 SECTION("number (integer)")
@@ -3070,6 +3190,16 @@ TEST_CASE("element access")
                     const json j_nonobject_const(j_nonobject);
                     CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
                     CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonobject.at("foo");
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with number");
+                    }
                 }
 
                 SECTION("number (floating-point)")
@@ -3078,6 +3208,16 @@ TEST_CASE("element access")
                     const json j_nonobject_const(j_nonobject);
                     CHECK_THROWS_AS(j_nonobject.at("foo"), std::domain_error);
                     CHECK_THROWS_AS(j_nonobject_const.at("foo"), std::domain_error);
+
+                    // exception name
+                    try
+                    {
+                        j_nonobject.at("foo");
+                    }
+                    catch (std::domain_error& e)
+                    {
+                        CHECK(std::string(e.what()) == "cannot use at() with number");
+                    }
                 }
             }
         }