Merge branch 'develop' into feature/userdefined_exceptions
diff --git a/README.md b/README.md
index 5849067..e16d881 100644
--- a/README.md
+++ b/README.md
@@ -486,6 +486,7 @@
 - [Mário Feroldi](https://github.com/thelostt) fixed a small typo.
 - [duncanwerner](https://github.com/duncanwerner) found a really embarrassing performance regression in the 2.0.0 release.
 - [Damien](https://github.com/dtoma) fixed one of the last conversion warnings.
+- [Muri Nicanor](https://github.com/murinicanor) made the Makefile more portable by choosing the correct version of sed.
 
 Thanks a lot for helping out!
 
@@ -505,7 +506,7 @@
 $ ./json_unit "*"
 
 ===============================================================================
-All tests passed (8905012 assertions in 32 test cases)
+All tests passed (8905087 assertions in 32 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/is_number_signed.cpp b/doc/examples/is_number_signed.cpp
new file mode 100644
index 0000000..dd0214f
--- /dev/null
+++ b/doc/examples/is_number_signed.cpp
@@ -0,0 +1,27 @@
+#include <json.hpp>
+
+using json = nlohmann::json;
+
+int main()
+{
+    // create JSON values
+    json j_null;
+    json j_boolean = true;
+    json j_number_integer = 17;
+    json j_number_unsigned_integer = 12345678987654321u;
+    json j_number_float = 23.42;
+    json j_object = {{"one", 1}, {"two", 2}};
+    json j_array = {1, 2, 4, 8, 16};
+    json j_string = "Hello, world";
+
+    // call is_number_signed()
+    std::cout << std::boolalpha;
+    std::cout << j_null.is_number_signed() << '\n';
+    std::cout << j_boolean.is_number_signed() << '\n';
+    std::cout << j_number_integer.is_number_signed() << '\n';
+    std::cout << j_number_unsigned_integer.is_number_signed() << '\n';
+    std::cout << j_number_float.is_number_signed() << '\n';
+    std::cout << j_object.is_number_signed() << '\n';
+    std::cout << j_array.is_number_signed() << '\n';
+    std::cout << j_string.is_number_signed() << '\n';
+}
diff --git a/doc/examples/is_number_signed.link b/doc/examples/is_number_signed.link
new file mode 100644
index 0000000..920b7b0
--- /dev/null
+++ b/doc/examples/is_number_signed.link
@@ -0,0 +1 @@
+<a target="_blank" href="http://melpon.org/wandbox/permlink/i2UvvUEA0oZ3JeQZ"><b>online</b></a>
\ No newline at end of file
diff --git a/doc/examples/is_number_signed.output b/doc/examples/is_number_signed.output
new file mode 100644
index 0000000..d126b27
--- /dev/null
+++ b/doc/examples/is_number_signed.output
@@ -0,0 +1,8 @@
+false
+false
+true
+false
+false
+false
+false
+false
diff --git a/src/json.hpp b/src/json.hpp
index 9d2305d..4de4ff4 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 2.0.1
+|  |  |__   |  |  | | | |  version 3.0.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -265,6 +265,100 @@
 
     /// @}
 
+  public:
+    /////////////////////////////
+    // user-defined exceptions //
+    /////////////////////////////
+
+    /// @name exception types
+    /// Classes for exceptions used by the library functions.
+    /// @{
+
+    /*!
+    @brief a user-defined exception class
+
+    To implement user-defined exceptions for the library, this class is used
+    as subclass for different exception types. Compared to `std::exception`,
+    the class also contains an error code to better reference and document
+    exceptions.
+
+    error code | exception type | message | description
+    ---------- | -------------- | ------- | -----------
+    101 | @ref invalid_iterator | "iterators are not compatible" | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+    102 | @ref invalid_iterator | "iterators do not fit current value" | Either iterator passed to function @ref erase(InteratorType first, InteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
+    103 | @ref invalid_iterator | "iterator does not fit current value" | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
+    104 | @ref invalid_iterator | "iterators out of range" | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
+    105 | @ref invalid_iterator | "iterator out of range" | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
+    106 | @ref invalid_iterator | "cannot construct with iterators from null" | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
+    107 | @ref invalid_iterator | "cannot use key() for non-object iterators" | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
+    108 | @ref invalid_iterator | "cannot use operator[] for object iterators" | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+    109 | @ref invalid_iterator | "cannot use offsets with object iterators" | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+    110 | @ref invalid_iterator | "iterators do not fit" | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+    111 | @ref invalid_iterator | "passed iterators may not belong to container" | The iterator range passed to the insert function must not be a subrange of the container to insert to.
+    112 | @ref invalid_iterator | "cannot compare iterators of different containers" | When two iterators are compared, they must belong to the same container.
+    113 | @ref invalid_iterator | "cannot compare order of object iterators" | The order of object iterators cannot be compated, because JSON objects are unordered.
+    114 | @ref invalid_iterator | "cannot get value" | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
+    */
+    class exception : public std::exception
+    {
+      public:
+        /*!
+        @brief create a user-defined exception with description and error code
+
+        @param[in] error_code a code for the exception
+        @param[in] what_arg a description for the exception
+        */
+        exception(int error_code, const std::string& what_arg)
+            : std::exception(),
+              message("[except." + std::to_string(error_code) + "] " + what_arg),
+              ecode(error_code)
+        {}
+
+        /// returns the explanatory string
+        const char* what() const noexcept
+        {
+            return message.c_str();
+        }
+
+        /// returns the error code
+        int error_code() const noexcept
+        {
+            return ecode;
+        }
+
+      private:
+        /// the explanatory string for the exception
+        const std::string message {};
+        /// the error code of the exception
+        const int ecode = -1;
+    };
+
+    /// class for iterator-related exceptions (error code 1xx)
+    class invalid_iterator : public exception
+    {
+        using exception::exception;
+    };
+
+    /// class for parser-related exceptions (error code 2xx)
+    class parse_error : public exception
+    {
+        using exception::exception;
+    };
+
+    /// class for type-related exceptions (error code 3xx)
+    class type_error : public exception
+    {
+        using exception::exception;
+    };
+
+    /// class for extension-related exceptions (error code 4xx)
+    class extension_error : public exception
+    {
+        using exception::exception;
+    };
+
+    /// @}
+
 
     /*!
     @brief returns the allocator associated with the container
@@ -1597,7 +1691,7 @@
     value_t::array and @ref value_t::object are valid); when @a type_deduction
     is set to `true`, this parameter has no effect
 
-    @throw std::domain_error if @a type_deduction is `false`, @a manual_type
+    @throw type_error (301) if @a type_deduction is `false`, @a manual_type
     is `value_t::object`, but @a init contains an element which is not a pair
     whose first element is a string; example: `"cannot create object from
     initializer list"`
@@ -1638,7 +1732,7 @@
             // if object is wanted but impossible, throw an exception
             if (manual_type == value_t::object and not is_an_object)
             {
-                throw std::domain_error("cannot create object from initializer list");
+                throw type_error(301, "cannot create object from initializer list");
             }
         }
 
@@ -1721,7 +1815,7 @@
 
     @return JSON object value
 
-    @throw std::domain_error if @a init is not a pair whose first elements are
+    @throw type_error (301) if @a init is not a pair whose first elements are
     strings; thrown by
     @ref basic_json(std::initializer_list<basic_json>, bool, value_t)
 
@@ -1785,14 +1879,15 @@
     @param[in] first begin of the range to copy from (included)
     @param[in] last end of the range to copy from (excluded)
 
-    @throw std::domain_error if iterators are not compatible; that is, do not
-    belong to the same JSON value; example: `"iterators are not compatible"`
-    @throw std::out_of_range if iterators are for a primitive type (number,
+    @throw invalid_iterator (101) if iterators are not compatible; that is, do
+    not belong to the same JSON value; example: `"iterators are not
+    compatible"`
+    @throw invalid_iterator (104) if iterators are for a primitive type (number,
     boolean, or string) where an out of range error can be detected easily;
     example: `"iterators out of range"`
     @throw std::bad_alloc if allocation for object, array, or string fails
-    @throw std::domain_error if called with a null value; example: `"cannot
-    use construct with iterators from null"`
+    @throw invalid_iterator (106) if called with a null value; example: `"cannot
+    construct with iterators from null"`
 
     @complexity Linear in distance between @a first and @a last.
 
@@ -1812,7 +1907,7 @@
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators are not compatible");
+            throw invalid_iterator(101, "iterators are not compatible");
         }
 
         // check if iterator range is complete for primitive values
@@ -1826,7 +1921,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    throw invalid_iterator(104, "iterators out of range");
                 }
                 break;
             }
@@ -1889,7 +1984,8 @@
             default:
             {
                 assert(first.m_object != nullptr);
-                throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
+                throw invalid_iterator(106, "cannot use construct with iterators from " +
+                                       first.m_object->type_name());
             }
         }
     }
@@ -2358,6 +2454,34 @@
     }
 
     /*!
+    @brief return whether value is a signed integer number
+
+    This function returns true iff the JSON value is a signed integer
+    number. This excludes floating-point and (unsigned) integer values.
+
+    @return `true` if type is a signed integer number, `false` otherwise.
+
+    @complexity Constant.
+
+    @exceptionsafety No-throw guarantee: this member function never throws
+    exceptions.
+
+    @liveexample{The following code exemplifies `is_number_signed()` for all
+    JSON types.,is_number_signed}
+
+    @sa @ref is_number() -- check if value is a number
+    @sa @ref is_number_integer() -- check if value is an integer or unsigned
+    integer number
+    @sa @ref is_number_float() -- check if value is a floating-point number
+
+    @since version 3.0.0
+    */
+    constexpr bool is_number_signed() const noexcept
+    {
+        return m_type == value_t::number_integer;
+    }
+
+    /*!
     @brief return whether value is an unsigned integer number
 
     This function returns true iff the JSON value is an unsigned integer
@@ -2551,7 +2675,7 @@
         }
         else
         {
-            throw std::domain_error("type must be object, but is " + type_name());
+            throw type_error(302, "type must be object, but is " + type_name());
         }
     }
 
@@ -2565,7 +2689,7 @@
         }
         else
         {
-            throw std::domain_error("type must be object, but is " + type_name());
+            throw type_error(302, "type must be object, but is " + type_name());
         }
     }
 
@@ -2593,7 +2717,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2619,7 +2743,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2638,7 +2762,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2652,7 +2776,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2670,7 +2794,7 @@
         }
         else
         {
-            throw std::domain_error("type must be string, but is " + type_name());
+            throw type_error(302, "type must be string, but is " + type_name());
         }
     }
 
@@ -2700,7 +2824,7 @@
 
             default:
             {
-                throw std::domain_error("type must be number, but is " + type_name());
+                throw type_error(302, "type must be number, but is " + type_name());
             }
         }
     }
@@ -2710,7 +2834,7 @@
     {
         return is_boolean()
                ? m_value.boolean
-               : throw std::domain_error("type must be boolean, but is " + type_name());
+               : throw type_error(302, "type must be boolean, but is " + type_name());
     }
 
     /// get a pointer to the value (object)
@@ -2764,13 +2888,13 @@
     /// get a pointer to the value (integer number)
     number_integer_t* get_impl_ptr(number_integer_t*) noexcept
     {
-        return is_number_integer() ? &m_value.number_integer : nullptr;
+        return is_number_signed() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (integer number)
     constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept
     {
-        return is_number_integer() ? &m_value.number_integer : nullptr;
+        return is_number_signed() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
@@ -2805,8 +2929,9 @@
 
     @tparam ThisType will be deduced as `basic_json` or `const basic_json`
 
-    @throw std::domain_error if ReferenceType does not match underlying value
-    type of the current JSON
+    @throw type_error (310) if ReferenceType does not match underlying value
+    type of the current JSON; example: `"incompatible ReferenceType for
+    get_ref, actual type is object"`
     */
     template<typename ReferenceType, typename ThisType>
     static ReferenceType get_ref_impl(ThisType& obj)
@@ -2823,8 +2948,8 @@
         }
         else
         {
-            throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
-                                    obj.type_name());
+            throw type_error(310, "incompatible ReferenceType for get_ref, actual type is " +
+                             obj.type_name());
         }
     }
 
@@ -2845,7 +2970,7 @@
 
     @return copy of the JSON value, converted to type @a ValueType
 
-    @throw std::domain_error in case passed type @a ValueType is incompatible
+    @throw type_error (302) in case passed type @a ValueType is incompatible
     to JSON; example: `"type must be object, but is null"`
 
     @complexity Linear in the size of the JSON value.
@@ -3025,7 +3150,7 @@
     reference type @a ReferenceType fits to the JSON value; throws
     std::domain_error otherwise
 
-    @throw std::domain_error in case passed type @a ReferenceType is
+    @throw type_error (310) in case passed type @a ReferenceType is
     incompatible with the stored JSON value
 
     @complexity Constant.
@@ -3073,7 +3198,7 @@
 
     @return copy of the JSON value, converted to type @a ValueType
 
-    @throw std::domain_error in case passed type @a ValueType is incompatible
+    @throw type_error (310) in case passed type @a ValueType is incompatible
     to JSON, thrown by @ref get() const
 
     @complexity Linear in the size of the JSON value.
@@ -3122,7 +3247,7 @@
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if the JSON value is not an array; example:
+    @throw type_error (303) 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()`; example: `"array index 7 is out of range"`
@@ -3152,7 +3277,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3166,7 +3291,7 @@
 
     @return const reference to the element at index @a idx
 
-    @throw std::domain_error if the JSON value is not an array; example:
+    @throw type_error (303) 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()`; example: `"array index 7 is out of range"`
@@ -3196,7 +3321,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3210,7 +3335,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if the JSON value is not an object; example:
+    @throw type_error (303) 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()`; example: `"key "the fast" not found"`
@@ -3244,7 +3369,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3258,7 +3383,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if the JSON value is not an object; example:
+    @throw type_error (303) 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()`; example: `"key "the fast" not found"`
@@ -3292,7 +3417,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3309,7 +3434,7 @@
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array or null; example:
+    @throw type_error (304) if JSON is not an array or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Constant if @a idx is in the range of the array. Otherwise
@@ -3346,7 +3471,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3359,7 +3484,7 @@
 
     @return const reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array; example: `"cannot use
+    @throw type_error (304) if JSON is not an array; example: `"cannot use
     operator[] with null"`
 
     @complexity Constant.
@@ -3379,7 +3504,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3396,7 +3521,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object or null; example:
+    @throw type_error (304) if JSON is not an object or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Logarithmic in the size of the container.
@@ -3427,7 +3552,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3444,7 +3569,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (304) if JSON is not an object; example: `"cannot use
     operator[] with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3469,7 +3594,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3486,7 +3611,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object or null; example:
+    @throw type_error (304) if JSON is not an object or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Logarithmic in the size of the container.
@@ -3521,7 +3646,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (304) if JSON is not an object; example: `"cannot use
     operator[] with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3554,7 +3679,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object or null; example:
+    @throw type_error (304) if JSON is not an object or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Logarithmic in the size of the container.
@@ -3586,7 +3711,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3603,7 +3728,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (304) if JSON is not an object; example: `"cannot use
     operator[] with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3629,7 +3754,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3666,7 +3791,7 @@
     @return copy of the element at key @a key or @a default_value if @a key
     is not found
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (305) if JSON is not an object; example: `"cannot use
     value() with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3703,7 +3828,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use value() with " + type_name());
+            throw type_error(305, "cannot use value() with " + type_name());
         }
     }
 
@@ -3745,7 +3870,7 @@
     @return copy of the element at key @a key or @a default_value if @a key
     is not found
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (305) if JSON is not an object; example: `"cannot use
     value() with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3778,7 +3903,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use value() with " + type_name());
+            throw type_error(305, "cannot use value() with " + type_name());
         }
     }
 
@@ -3807,7 +3932,7 @@
     or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
-    @throw std::out_of_range when called on `null` value
+    @throw invalid_iterator (114) when called on `null` value
 
     @liveexample{The following code shows an example for `front()`.,front}
 
@@ -3849,7 +3974,7 @@
     or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
-    @throw std::out_of_range when called on `null` value.
+    @throw invalid_iterator (114) when called on `null` value
 
     @liveexample{The following code shows an example for `back()`.,back}
 
@@ -3893,11 +4018,12 @@
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot
+    @throw type_error (306) if called on a `null` value; example: `"cannot
     use erase() with null"`
-    @throw std::domain_error if called on an iterator which does not belong to
-    the current JSON value; example: `"iterator does not fit current value"`
-    @throw std::out_of_range if called on a primitive type with invalid
+    @throw invalid_iterator (103) if called on an iterator which does not belong
+    to the current JSON value; example: `"iterator does not fit current
+    value"`
+    @throw invalid_iterator (105) if called on a primitive type with invalid
     iterator (i.e., any iterator which is not `begin()`); example: `"iterator
     out of range"`
 
@@ -3930,7 +4056,7 @@
         // make sure iterator fits the current value
         if (this != pos.m_object)
         {
-            throw std::domain_error("iterator does not fit current value");
+            throw invalid_iterator(103, "iterator does not fit current value");
         }
 
         InteratorType result = end();
@@ -3945,7 +4071,7 @@
             {
                 if (not pos.m_it.primitive_iterator.is_begin())
                 {
-                    throw std::out_of_range("iterator out of range");
+                    throw invalid_iterator(105, "iterator out of range");
                 }
 
                 if (is_string())
@@ -3974,7 +4100,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                throw type_error(306, "cannot use erase() with " + type_name());
             }
         }
 
@@ -4001,11 +4127,11 @@
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot
+    @throw type_error (306) if called on a `null` value; example: `"cannot
     use erase() with null"`
-    @throw std::domain_error if called on iterators which does not belong to
-    the current JSON value; example: `"iterators do not fit current value"`
-    @throw std::out_of_range if called on a primitive type with invalid
+    @throw invalid_iterator (102) if called on iterators which does not belong
+    to the current JSON value; example: `"iterators do not fit current value"`
+    @throw invalid_iterator (104) if called on a primitive type with invalid
     iterators (i.e., if `first != begin()` and `last != end()`); example:
     `"iterators out of range"`
 
@@ -4038,7 +4164,7 @@
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
         {
-            throw std::domain_error("iterators do not fit current value");
+            throw invalid_iterator(102, "iterators do not fit current value");
         }
 
         InteratorType result = end();
@@ -4053,7 +4179,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    throw invalid_iterator(104, "iterators out of range");
                 }
 
                 if (is_string())
@@ -4084,7 +4210,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                throw type_error(306, "cannot use erase() with " + type_name());
             }
         }
 
@@ -4105,7 +4231,7 @@
     @post References and iterators to the erased elements are invalidated.
     Other references and iterators are not affected.
 
-    @throw std::domain_error when called on a type other than JSON object;
+    @throw type_error (306) when called on a type other than JSON object;
     example: `"cannot use erase() with null"`
 
     @complexity `log(size()) + count(key)`
@@ -4130,7 +4256,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use erase() with " + type_name());
+            throw type_error(306, "cannot use erase() with " + type_name());
         }
     }
 
@@ -4141,7 +4267,7 @@
 
     @param[in] idx index of the element to remove
 
-    @throw std::domain_error when called on a type other than JSON array;
+    @throw type_error (306) when called on a type other than JSON array;
     example: `"cannot use erase() with null"`
     @throw std::out_of_range when `idx >= size()`; example: `"array index 17
     is out of range"`
@@ -4173,7 +4299,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use erase() with " + type_name());
+            throw type_error(306, "cannot use erase() with " + type_name());
         }
     }
 
@@ -4883,7 +5009,7 @@
 
     @param[in] val the value to add to the JSON array
 
-    @throw std::domain_error when called on a type other than JSON array or
+    @throw type_error (307) when called on a type other than JSON array or
     null; example: `"cannot use push_back() with number"`
 
     @complexity Amortized constant.
@@ -4899,7 +5025,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            throw type_error(307, "cannot use push_back() with " + type_name());
         }
 
         // transform null object into an array
@@ -4935,7 +5061,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            throw type_error(307, "cannot use push_back() with " + type_name());
         }
 
         // transform null object into an array
@@ -4969,7 +5095,7 @@
 
     @param[in] val the value to add to the JSON object
 
-    @throw std::domain_error when called on a type other than JSON object or
+    @throw type_error (307) when called on a type other than JSON object or
     null; example: `"cannot use push_back() with number"`
 
     @complexity Logarithmic in the size of the container, O(log(`size()`)).
@@ -4985,7 +5111,7 @@
         // push_back only works for null objects or objects
         if (not(is_null() or is_object()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            throw type_error(307, "cannot use push_back() with " + type_name());
         }
 
         // transform null object into an object
@@ -5068,10 +5194,10 @@
     @param[in] val element to insert
     @return iterator pointing to the inserted @a val.
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
 
     @complexity Constant plus linear in the distance between pos and end of the
     container.
@@ -5088,7 +5214,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                throw invalid_iterator(103, "iterator does not fit current value");
             }
 
             // insert to array and return iterator
@@ -5099,7 +5225,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
     }
 
@@ -5124,10 +5250,10 @@
     @return iterator pointing to the first element inserted, or @a pos if
     `cnt==0`
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
 
     @complexity Linear in @a cnt plus linear in the distance between @a pos
     and end of the container.
@@ -5144,7 +5270,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                throw invalid_iterator(103, "iterator does not fit current value");
             }
 
             // insert to array and return iterator
@@ -5155,7 +5281,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
     }
 
@@ -5169,13 +5295,13 @@
     @param[in] first begin of the range of elements to insert
     @param[in] last end of the range of elements to insert
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
-    @throw std::domain_error if @a first and @a last do not belong to the same
-    JSON value; example: `"iterators do not fit"`
-    @throw std::domain_error if @a first or @a last are iterators into
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
+    @throw invalid_iterator (110) if @a first and @a last do not belong to the
+    same JSON value; example: `"iterators do not fit"`
+    @throw invalid_iterator (111) if @a first or @a last are iterators into
     container for which insert is called; example: `"passed iterators may not
     belong to container"`
 
@@ -5194,24 +5320,25 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            throw invalid_iterator(103, "iterator does not fit current value");
         }
 
         // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators do not fit");
+            throw invalid_iterator(110, "iterators do not fit");
         }
 
+        // first and last iterator must not belong to this container
         if (first.m_object == this or last.m_object == this)
         {
-            throw std::domain_error("passed iterators may not belong to container");
+            throw invalid_iterator(111, "passed iterators may not belong to container");
         }
 
         // insert to array and return iterator
@@ -5233,10 +5360,10 @@
     the end() iterator
     @param[in] ilist initializer list to insert the values from
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
 
     @return iterator pointing to the first element inserted, or @a pos if
     `ilist` is empty
@@ -5253,13 +5380,13 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            throw invalid_iterator(103, "iterator does not fit current value");
         }
 
         // insert to array and return iterator
@@ -5307,8 +5434,8 @@
 
     @param[in,out] other array to exchange the contents with
 
-    @throw std::domain_error when JSON value is not an array; example: `"cannot
-    use swap() with string"`
+    @throw type_error (308) when JSON value is not an array; example:
+    `"cannot use swap() with string"`
 
     @complexity Constant.
 
@@ -5327,7 +5454,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            throw type_error(308, "cannot use swap() with " + type_name());
         }
     }
 
@@ -5341,7 +5468,7 @@
 
     @param[in,out] other object to exchange the contents with
 
-    @throw std::domain_error when JSON value is not an object; example:
+    @throw type_error (308) when JSON value is not an object; example:
     `"cannot use swap() with string"`
 
     @complexity Constant.
@@ -5361,7 +5488,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            throw type_error(308, "cannot use swap() with " + type_name());
         }
     }
 
@@ -5375,7 +5502,7 @@
 
     @param[in,out] other string to exchange the contents with
 
-    @throw std::domain_error when JSON value is not a string; example: `"cannot
+    @throw type_error (308) when JSON value is not a string; example: `"cannot
     use swap() with boolean"`
 
     @complexity Constant.
@@ -5395,7 +5522,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            throw type_error(308, "cannot use swap() with " + type_name());
         }
     }
 
@@ -5899,6 +6026,8 @@
 
     @return result of the deserialization
 
+    @throw parse_error (2xx) in case of parse errors
+
     @complexity Linear in the length of the input. The parser is a predictive
     LL(1) parser. The complexity can be higher if the parser callback function
     @a cb has a super-linear complexity.
@@ -5927,6 +6056,8 @@
     which is used to control the deserialization by filtering unwanted values
     (optional)
 
+    @throw parse_error (2xx) in case of parse errors
+
     @return result of the deserialization
 
     @complexity Linear in the length of the input. The parser is a predictive
@@ -5966,7 +6097,7 @@
     @param[in,out] i  input stream to read a serialized JSON value from
     @param[in,out] j  JSON value to write the deserialized input to
 
-    @throw std::invalid_argument in case of parse errors
+    @throw parse_error (2xx) in case of parse errors
 
     @complexity Linear in the length of the input. The parser is a predictive
     LL(1) parser.
@@ -6752,7 +6883,7 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    throw invalid_iterator(114, "cannot get value");
                 }
 
                 default:
@@ -6763,7 +6894,7 @@
                     }
                     else
                     {
-                        throw std::out_of_range("cannot get value");
+                        throw invalid_iterator(114, "cannot get value");
                     }
                 }
             }
@@ -6798,7 +6929,7 @@
                     }
                     else
                     {
-                        throw std::out_of_range("cannot get value");
+                        throw invalid_iterator(114, "cannot get value");
                     }
                 }
             }
@@ -6884,7 +7015,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                throw invalid_iterator(112, "cannot compare iterators of different containers");
             }
 
             assert(m_object != nullptr);
@@ -6920,7 +7051,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                throw invalid_iterator(112, "cannot compare iterators of different containers");
             }
 
             assert(m_object != nullptr);
@@ -6929,7 +7060,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot compare order of object iterators");
+                    throw invalid_iterator(113, "cannot compare order of object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -6971,7 +7102,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    throw invalid_iterator(109, "cannot use offsets with object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -7021,7 +7152,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    throw invalid_iterator(109, "cannot use offsets with object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -7045,7 +7176,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use operator[] for object iterators");
+                    throw invalid_iterator(108, "cannot use operator[] for object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -7055,7 +7186,7 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    throw invalid_iterator(114, "cannot get value");
                 }
 
                 default:
@@ -7066,7 +7197,7 @@
                     }
                     else
                     {
-                        throw std::out_of_range("cannot get value");
+                        throw invalid_iterator(114, "cannot get value");
                     }
                 }
             }
@@ -7083,7 +7214,7 @@
             }
             else
             {
-                throw std::domain_error("cannot use key() for non-object iterators");
+                throw invalid_iterator(107, "cannot use key() for non-object iterators");
             }
         }
 
@@ -7391,7 +7522,7 @@
         {
             m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
-            m_start = m_cursor = m_content;
+            m_buffer_start = m_start = m_cursor = m_content;
             m_limit = m_content + s.size();
         }
 
@@ -7403,7 +7534,7 @@
             std::getline(*m_stream, m_buffer);
             m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
-            m_start = m_cursor = m_content;
+            m_buffer_start = m_start = m_cursor = m_content;
             m_limit = m_content + m_buffer.size();
         }
 
@@ -7428,17 +7559,17 @@
         @return string representation of the code point; the length of the
         result string is between 1 and 4 characters.
 
-        @throw std::out_of_range if code point is > 0x10ffff; example: `"code
+        @throw parse_error (203) if code point is > 0x10ffff; example: `"code
         points above 0x10FFFF are invalid"`
-        @throw std::invalid_argument if the low surrogate is invalid; example:
-        `""missing or wrong low surrogate""`
+        @throw parse_error (202) if the low surrogate is invalid; example:
+        `"missing or wrong low surrogate"`
 
         @complexity Constant.
 
         @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code>
         */
-        static string_t to_unicode(const std::size_t codepoint1,
-                                   const std::size_t codepoint2 = 0)
+        string_t to_unicode(const std::size_t codepoint1,
+                            const std::size_t codepoint2 = 0) const
         {
             // calculate the code point from the given code points
             std::size_t codepoint = codepoint1;
@@ -7461,7 +7592,8 @@
                 }
                 else
                 {
-                    throw std::invalid_argument("missing or wrong low surrogate");
+                    const std::string error_msg = error_message() + "; missing or wrong low surrogate";
+                    throw parse_error(202, error_msg);
                 }
             }
 
@@ -7495,7 +7627,8 @@
             }
             else
             {
-                throw std::out_of_range("code points above 0x10FFFF are invalid");
+                const std::string error_msg = error_message() + "; code points above 0x10FFFF are invalid";
+                throw parse_error(203, error_msg);
             }
 
             return result;
@@ -8348,7 +8481,8 @@
         /// append data from the stream to the internal buffer
         void yyfill() noexcept
         {
-            if (m_stream == nullptr or not * m_stream)
+            // yyfill has no effect if we do not have a ready stream
+            if (m_stream == nullptr or m_stream->fail())
             {
                 return;
             }
@@ -8357,20 +8491,56 @@
             const auto offset_marker = m_marker - m_start;
             const auto offset_cursor = m_cursor - m_start;
 
-            m_buffer.erase(0, static_cast<size_t>(offset_start));
+            // get the size of the previous buffer
+            const auto last_buffer_size = static_cast<size_t>(offset_start);
+            // remmeber all buffer sizes so far
+            m_previous_buffer_sizes += last_buffer_size;
+
+            // delete previous buffer
+            m_buffer.erase(0, last_buffer_size);
+
+            // read line from stream to buffer
             std::string line;
             assert(m_stream != nullptr);
             std::getline(*m_stream, line);
             m_buffer += "\n" + line; // add line with newline symbol
 
+            // set the pointers accordingly
             m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
-            m_start  = m_content;
+            m_buffer_start = m_start = m_content;
             m_marker = m_start + offset_marker;
             m_cursor = m_start + offset_cursor;
             m_limit  = m_start + m_buffer.size() - 1;
         }
 
+        /// return the location of the end of the last token
+        size_t last_token_location() const noexcept
+        {
+            // m_buffer_start points to the first byte of the current buffer
+            // m_cursor points to the byte behind the current token
+            // m_previous_buffer_sizes contains the size the previous buffers
+            return static_cast<size_t>(m_cursor - m_buffer_start) + m_previous_buffer_sizes;
+        }
+
+        /// returns an error message for parse errors
+        std::string error_message() const
+        {
+            const std::string last_token(reinterpret_cast<typename std::string::const_pointer>(m_start),
+                                         static_cast<size_t>(m_cursor - m_start));
+            std::string result = "parse error: unexpected ";
+            if (last_token_type == token_type::end_of_input)
+            {
+                result += token_type_name(token_type::end_of_input);
+            }
+            else
+            {
+                result += "'" + last_token + "'";
+            }
+            result += " at byte " + std::to_string(last_token_location() - last_token.size() + 1);
+            return result;
+        }
+
         /// return string representation of last read token
         string_t get_token_string() const
         {
@@ -8434,7 +8604,9 @@
 
         @return string value of current token without opening and closing
         quotes
-        @throw std::out_of_range if to_unicode fails
+
+        @throw parse_error (202,203) if to_unicode fails
+        @throw parse_error (204) if low surrogate is missing
         */
         string_t get_string() const
         {
@@ -8509,7 +8681,8 @@
                                 // make sure there is a subsequent unicode
                                 if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
                                 {
-                                    throw std::invalid_argument("missing low surrogate");
+                                    std::string error_msg = error_message() + "; missing low surrogate";
+                                    throw parse_error(204, error_msg);
                                 }
 
                                 // get code yyyy from uxxxx\uyyyy
@@ -8722,6 +8895,10 @@
         const lexer_char_t* m_cursor = nullptr;
         /// pointer to the end of the buffer
         const lexer_char_t* m_limit = nullptr;
+        /// pointer to the beginning of the buffer
+        const lexer_char_t* m_buffer_start = nullptr;
+        /// the size of the last buffer (before yyfill was called)
+        size_t m_previous_buffer_sizes = 0;
         /// the last token type
         token_type last_token_type = token_type::end_of_input;
     };
@@ -8966,12 +9143,9 @@
         {
             if (t != last_token)
             {
-                std::string error_msg = "parse error - unexpected ";
-                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
-                              "'") :
-                              lexer::token_type_name(last_token));
+                std::string error_msg = m_lexer.error_message();
                 error_msg += "; expected " + lexer::token_type_name(t);
-                throw std::invalid_argument(error_msg);
+                throw parse_error(201, error_msg);
             }
         }
 
@@ -8979,11 +9153,9 @@
         {
             if (t == last_token)
             {
-                std::string error_msg = "parse error - unexpected ";
-                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
-                              "'") :
-                              lexer::token_type_name(last_token));
-                throw std::invalid_argument(error_msg);
+                std::string error_msg = m_lexer.error_message();
+                error_msg += "; expected JSON value";
+                throw parse_error(201, error_msg);
             }
         }
 
@@ -9026,10 +9198,10 @@
                       empty string is assumed which references the whole JSON
                       value
 
-        @throw std::domain_error if reference token is nonempty and does not
-        begin with a slash (`/`); example: `"JSON pointer must be empty or
+        @throw extension_error (403) if reference token is nonempty and does
+        not begin with a slash (`/`); example: `"JSON pointer must be empty or
         begin with /"`
-        @throw std::domain_error if a tilde (`~`) is not followed by `0`
+        @throw extension_error (404) if a tilde (`~`) is not followed by `0`
         (representing `~`) or `1` (representing `/`); example: `"escape error:
         ~ must be followed with 0 or 1"`
 
@@ -9079,7 +9251,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                throw extension_error(401, "JSON pointer has no parent");
             }
 
             auto last = reference_tokens.back();
@@ -9097,7 +9269,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                throw extension_error(401, "JSON pointer has no parent");
             }
 
             json_pointer result = *this;
@@ -9158,7 +9330,7 @@
                     */
                     default:
                     {
-                        throw std::domain_error("invalid value to unflatten");
+                        throw extension_error(411, "invalid value to unflatten");
                     }
                 }
             }
@@ -9176,8 +9348,10 @@
         @complexity Linear in the length of the JSON pointer.
 
         @throw std::out_of_range      if the JSON pointer can not be resolved
-        @throw std::domain_error      if an array index begins with '0'
-        @throw std::invalid_argument  if an array index was not a number
+        @throw extension_error (402)  if an array index begins with '0' or is
+        not a number
+        @throw extension_error (405)  if the reference token could not be
+        resolved
         */
         reference get_unchecked(pointer ptr) const
         {
@@ -9197,7 +9371,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         if (reference_token == "-")
@@ -9208,14 +9382,24 @@
                         else
                         {
                             // convert array index to number; unchecked access
-                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                            try
+                            {
+                                ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                            }
+                            catch (std::invalid_argument&)
+                            {
+                                // std::stoi throws std::invalid_argument if
+                                // no conversion could be performed; we catch
+                                // it for better diagnosis
+                                throw extension_error(402, "array index is '" + reference_token + "', but must be a number");
+                            }
                         }
                         break;
                     }
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -9241,15 +9425,15 @@
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            throw std::out_of_range("array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range");
+                            throw extension_error(406, "array index '-' (" +
+                                                  std::to_string(ptr->m_value.array->size()) +
+                                                  ") is out of range");
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         // note: at performs range check
@@ -9259,7 +9443,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -9293,15 +9477,15 @@
                         if (reference_token == "-")
                         {
                             // "-" cannot be used for const access
-                            throw std::out_of_range("array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range");
+                            throw extension_error(406, "array index '-' (" +
+                                                  std::to_string(ptr->m_value.array->size()) +
+                                                  ") is out of range");
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         // use unchecked array access
@@ -9311,7 +9495,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -9337,15 +9521,15 @@
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            throw std::out_of_range("array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range");
+                            throw extension_error(406, "array index '-' (" +
+                                                  std::to_string(ptr->m_value.array->size()) +
+                                                  ") is out of range");
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         // note: at performs range check
@@ -9355,7 +9539,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -9377,7 +9561,7 @@
             // check if nonempty reference string begins with slash
             if (reference_string[0] != '/')
             {
-                throw std::domain_error("JSON pointer must be empty or begin with '/'");
+                throw extension_error(403, "JSON pointer must be empty or begin with '/'");
             }
 
             // extract the reference tokens:
@@ -9412,13 +9596,13 @@
                             (reference_token[pos + 1] != '0' and
                              reference_token[pos + 1] != '1'))
                     {
-                        throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
+                        throw extension_error(404, "escape error: '~' must be followed with '0' or '1'");
                     }
                 }
 
                 // finally, store the reference token
                 unescape(reference_token);
-                result.push_back(reference_token);
+                result.emplace_back(reference_token);
             }
 
             return result;
@@ -9540,7 +9724,7 @@
         {
             if (not value.is_object())
             {
-                throw std::domain_error("only objects can be unflattened");
+                throw extension_error(412, "only objects can be unflattened");
             }
 
             basic_json result;
@@ -9551,7 +9735,7 @@
             {
                 if (not element.second.is_primitive())
                 {
-                    throw std::domain_error("values in object must be primitive");
+                    throw extension_error(413, "values in object must be primitive");
                 }
 
                 // assign value to reference pointed to by JSON pointer; Note
@@ -9603,8 +9787,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,operatorjson_pointer}
 
@@ -9630,8 +9814,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
 
@@ -9655,8 +9839,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,at_json_pointer}
 
@@ -9680,8 +9864,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,at_json_pointer_const}
 
@@ -9781,8 +9965,11 @@
     @throw std::out_of_range if a JSON pointer inside the patch could not
     be resolved successfully in the current JSON value; example: `"key baz
     not found"`
-    @throw invalid_argument if the JSON patch is malformed (e.g., mandatory
-    attributes are missing); example: `"operation add must have member path"`
+    @throw extension_error (408) if the JSON patch is malformed (e.g.,
+    mandatory attributes are missing); example: `"operation add must have
+    string member path"`
+    @throw extension_error (407) if the JSON patch is malformed; example:
+    `"JSON patch must be an array of objects"`
 
     @complexity Linear in the size of the JSON value and the length of the
     JSON patch. As usually only a fraction of the JSON value is affected by
@@ -9880,7 +10067,7 @@
                             if (static_cast<size_type>(idx) > parent.size())
                             {
                                 // avoid undefined behavior
-                                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                                throw extension_error(406, "array index " + std::to_string(idx) + " is out of range");
                             }
                             else
                             {
@@ -9932,7 +10119,7 @@
         if (not json_patch.is_array())
         {
             // a JSON patch must be an array of objects
-            throw std::invalid_argument("JSON patch must be an array of objects");
+            throw extension_error(407, "JSON patch must be an array of objects");
         }
 
         // iterate and apply th eoperations
@@ -9952,13 +10139,13 @@
                 // check if desired value is present
                 if (it == val.m_value.object->end())
                 {
-                    throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+                    throw extension_error(408, error_msg + " must have string member '" + member + "'");
                 }
 
                 // check if result is of type string
                 if (string_type and not it->second.is_string())
                 {
-                    throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+                    throw extension_error(408, error_msg + " must have string member '" + member + "'");
                 }
 
                 // no error: return value
@@ -9968,7 +10155,7 @@
             // type check
             if (not val.is_object())
             {
-                throw std::invalid_argument("JSON patch must be an array of objects");
+                throw extension_error(407, "JSON patch must be an array of objects");
             }
 
             // collect mandatory members
@@ -10041,7 +10228,7 @@
                     // throw an exception if test fails
                     if (not success)
                     {
-                        throw std::domain_error("unsuccessful: " + val.dump());
+                        throw extension_error(410, "unsuccessful: " + val.dump());
                     }
 
                     break;
@@ -10051,7 +10238,7 @@
                 {
                     // op must be "add", "remove", "replace", "move", "copy", or
                     // "test"
-                    throw std::invalid_argument("operation value '" + op + "' is invalid");
+                    throw extension_error(409, "operation value '" + op + "' is invalid");
                 }
             }
         }
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 537d4f4..7a41102 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++
-|  |  |__   |  |  | | | |  version 2.0.1
+|  |  |__   |  |  | | | |  version 3.0.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -265,6 +265,100 @@
 
     /// @}
 
+  public:
+    /////////////////////////////
+    // user-defined exceptions //
+    /////////////////////////////
+
+    /// @name exception types
+    /// Classes for exceptions used by the library functions.
+    /// @{
+
+    /*!
+    @brief a user-defined exception class
+
+    To implement user-defined exceptions for the library, this class is used
+    as subclass for different exception types. Compared to `std::exception`,
+    the class also contains an error code to better reference and document
+    exceptions.
+
+    error code | exception type | message | description
+    ---------- | -------------- | ------- | -----------
+    101 | @ref invalid_iterator | "iterators are not compatible" | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+    102 | @ref invalid_iterator | "iterators do not fit current value" | Either iterator passed to function @ref erase(InteratorType first, InteratorType last) does not belong to the JSON value from which values shall be erased. It hence does not define a valid range to delete values from.
+    103 | @ref invalid_iterator | "iterator does not fit current value" | In an erase or insert function, the passed iterator @a pos does not belong to the JSON value for which the function was called. It hence does not define a valid position for the deletion/insertion.
+    104 | @ref invalid_iterator | "iterators out of range" | When an iterator range for a primitive type (number, boolean, or string) is passed to a constructor or an erase function, this range has to be exactly (@ref begin(), @ref end()), because this is the only way the single stored value is expressed. All other ranges are invalid.
+    105 | @ref invalid_iterator | "iterator out of range" | When an iterator for a primitive type (number, boolean, or string) is passed to an erase function, the iterator has to be the @ref begin() iterator, because it is the only way to address the stored value. All other iterators are invalid.
+    106 | @ref invalid_iterator | "cannot construct with iterators from null" | The iterators passed to constructor @ref basic_json(InputIT first, InputIT last) belong to a JSON null value and hence to not define a valid range.
+    107 | @ref invalid_iterator | "cannot use key() for non-object iterators" | The key() member function can only be used on iterators belonging to a JSON object, because other types do not have a concept of a key.
+    108 | @ref invalid_iterator | "cannot use operator[] for object iterators" | The operator[] to specify a concrete offset cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+    109 | @ref invalid_iterator | "cannot use offsets with object iterators" | The offset operators (+, -, +=, -=) cannot be used on iterators belonging to a JSON object, because JSON objects are unordered.
+    110 | @ref invalid_iterator | "iterators do not fit" | The iterator range passed to the insert function are not compatible, meaning they do not belong to the same container. Therefore, the range (@a first, @a last) is invalid.
+    111 | @ref invalid_iterator | "passed iterators may not belong to container" | The iterator range passed to the insert function must not be a subrange of the container to insert to.
+    112 | @ref invalid_iterator | "cannot compare iterators of different containers" | When two iterators are compared, they must belong to the same container.
+    113 | @ref invalid_iterator | "cannot compare order of object iterators" | The order of object iterators cannot be compated, because JSON objects are unordered.
+    114 | @ref invalid_iterator | "cannot get value" | Cannot get value for iterator: Either the iterator belongs to a null value or it is an iterator to a primitive type (number, boolean, or string), but the iterator is different to @ref begin().
+    */
+    class exception : public std::exception
+    {
+      public:
+        /*!
+        @brief create a user-defined exception with description and error code
+
+        @param[in] error_code a code for the exception
+        @param[in] what_arg a description for the exception
+        */
+        exception(int error_code, const std::string& what_arg)
+            : std::exception(),
+              message("[except." + std::to_string(error_code) + "] " + what_arg),
+              ecode(error_code)
+        {}
+
+        /// returns the explanatory string
+        const char* what() const noexcept
+        {
+            return message.c_str();
+        }
+
+        /// returns the error code
+        int error_code() const noexcept
+        {
+            return ecode;
+        }
+
+      private:
+        /// the explanatory string for the exception
+        const std::string message {};
+        /// the error code of the exception
+        const int ecode = -1;
+    };
+
+    /// class for iterator-related exceptions (error code 1xx)
+    class invalid_iterator : public exception
+    {
+        using exception::exception;
+    };
+
+    /// class for parser-related exceptions (error code 2xx)
+    class parse_error : public exception
+    {
+        using exception::exception;
+    };
+
+    /// class for type-related exceptions (error code 3xx)
+    class type_error : public exception
+    {
+        using exception::exception;
+    };
+
+    /// class for extension-related exceptions (error code 4xx)
+    class extension_error : public exception
+    {
+        using exception::exception;
+    };
+
+    /// @}
+
 
     /*!
     @brief returns the allocator associated with the container
@@ -1597,7 +1691,7 @@
     value_t::array and @ref value_t::object are valid); when @a type_deduction
     is set to `true`, this parameter has no effect
 
-    @throw std::domain_error if @a type_deduction is `false`, @a manual_type
+    @throw type_error (301) if @a type_deduction is `false`, @a manual_type
     is `value_t::object`, but @a init contains an element which is not a pair
     whose first element is a string; example: `"cannot create object from
     initializer list"`
@@ -1638,7 +1732,7 @@
             // if object is wanted but impossible, throw an exception
             if (manual_type == value_t::object and not is_an_object)
             {
-                throw std::domain_error("cannot create object from initializer list");
+                throw type_error(301, "cannot create object from initializer list");
             }
         }
 
@@ -1721,7 +1815,7 @@
 
     @return JSON object value
 
-    @throw std::domain_error if @a init is not a pair whose first elements are
+    @throw type_error (301) if @a init is not a pair whose first elements are
     strings; thrown by
     @ref basic_json(std::initializer_list<basic_json>, bool, value_t)
 
@@ -1785,14 +1879,15 @@
     @param[in] first begin of the range to copy from (included)
     @param[in] last end of the range to copy from (excluded)
 
-    @throw std::domain_error if iterators are not compatible; that is, do not
-    belong to the same JSON value; example: `"iterators are not compatible"`
-    @throw std::out_of_range if iterators are for a primitive type (number,
+    @throw invalid_iterator (101) if iterators are not compatible; that is, do
+    not belong to the same JSON value; example: `"iterators are not
+    compatible"`
+    @throw invalid_iterator (104) if iterators are for a primitive type (number,
     boolean, or string) where an out of range error can be detected easily;
     example: `"iterators out of range"`
     @throw std::bad_alloc if allocation for object, array, or string fails
-    @throw std::domain_error if called with a null value; example: `"cannot
-    use construct with iterators from null"`
+    @throw invalid_iterator (106) if called with a null value; example: `"cannot
+    construct with iterators from null"`
 
     @complexity Linear in distance between @a first and @a last.
 
@@ -1812,7 +1907,7 @@
         // make sure iterator fits the current value
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators are not compatible");
+            throw invalid_iterator(101, "iterators are not compatible");
         }
 
         // check if iterator range is complete for primitive values
@@ -1826,7 +1921,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    throw invalid_iterator(104, "iterators out of range");
                 }
                 break;
             }
@@ -1889,7 +1984,8 @@
             default:
             {
                 assert(first.m_object != nullptr);
-                throw std::domain_error("cannot use construct with iterators from " + first.m_object->type_name());
+                throw invalid_iterator(106, "cannot use construct with iterators from " +
+                                       first.m_object->type_name());
             }
         }
     }
@@ -2358,6 +2454,34 @@
     }
 
     /*!
+    @brief return whether value is a signed integer number
+
+    This function returns true iff the JSON value is a signed integer
+    number. This excludes floating-point and (unsigned) integer values.
+
+    @return `true` if type is a signed integer number, `false` otherwise.
+
+    @complexity Constant.
+
+    @exceptionsafety No-throw guarantee: this member function never throws
+    exceptions.
+
+    @liveexample{The following code exemplifies `is_number_signed()` for all
+    JSON types.,is_number_signed}
+
+    @sa @ref is_number() -- check if value is a number
+    @sa @ref is_number_integer() -- check if value is an integer or unsigned
+    integer number
+    @sa @ref is_number_float() -- check if value is a floating-point number
+
+    @since version 3.0.0
+    */
+    constexpr bool is_number_signed() const noexcept
+    {
+        return m_type == value_t::number_integer;
+    }
+
+    /*!
     @brief return whether value is an unsigned integer number
 
     This function returns true iff the JSON value is an unsigned integer
@@ -2551,7 +2675,7 @@
         }
         else
         {
-            throw std::domain_error("type must be object, but is " + type_name());
+            throw type_error(302, "type must be object, but is " + type_name());
         }
     }
 
@@ -2565,7 +2689,7 @@
         }
         else
         {
-            throw std::domain_error("type must be object, but is " + type_name());
+            throw type_error(302, "type must be object, but is " + type_name());
         }
     }
 
@@ -2593,7 +2717,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2619,7 +2743,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2638,7 +2762,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2652,7 +2776,7 @@
         }
         else
         {
-            throw std::domain_error("type must be array, but is " + type_name());
+            throw type_error(302, "type must be array, but is " + type_name());
         }
     }
 
@@ -2670,7 +2794,7 @@
         }
         else
         {
-            throw std::domain_error("type must be string, but is " + type_name());
+            throw type_error(302, "type must be string, but is " + type_name());
         }
     }
 
@@ -2700,7 +2824,7 @@
 
             default:
             {
-                throw std::domain_error("type must be number, but is " + type_name());
+                throw type_error(302, "type must be number, but is " + type_name());
             }
         }
     }
@@ -2710,7 +2834,7 @@
     {
         return is_boolean()
                ? m_value.boolean
-               : throw std::domain_error("type must be boolean, but is " + type_name());
+               : throw type_error(302, "type must be boolean, but is " + type_name());
     }
 
     /// get a pointer to the value (object)
@@ -2764,13 +2888,13 @@
     /// get a pointer to the value (integer number)
     number_integer_t* get_impl_ptr(number_integer_t*) noexcept
     {
-        return is_number_integer() ? &m_value.number_integer : nullptr;
+        return is_number_signed() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (integer number)
     constexpr const number_integer_t* get_impl_ptr(const number_integer_t*) const noexcept
     {
-        return is_number_integer() ? &m_value.number_integer : nullptr;
+        return is_number_signed() ? &m_value.number_integer : nullptr;
     }
 
     /// get a pointer to the value (unsigned number)
@@ -2805,8 +2929,9 @@
 
     @tparam ThisType will be deduced as `basic_json` or `const basic_json`
 
-    @throw std::domain_error if ReferenceType does not match underlying value
-    type of the current JSON
+    @throw type_error (310) if ReferenceType does not match underlying value
+    type of the current JSON; example: `"incompatible ReferenceType for
+    get_ref, actual type is object"`
     */
     template<typename ReferenceType, typename ThisType>
     static ReferenceType get_ref_impl(ThisType& obj)
@@ -2823,8 +2948,8 @@
         }
         else
         {
-            throw std::domain_error("incompatible ReferenceType for get_ref, actual type is " +
-                                    obj.type_name());
+            throw type_error(310, "incompatible ReferenceType for get_ref, actual type is " +
+                             obj.type_name());
         }
     }
 
@@ -2845,7 +2970,7 @@
 
     @return copy of the JSON value, converted to type @a ValueType
 
-    @throw std::domain_error in case passed type @a ValueType is incompatible
+    @throw type_error (302) in case passed type @a ValueType is incompatible
     to JSON; example: `"type must be object, but is null"`
 
     @complexity Linear in the size of the JSON value.
@@ -3025,7 +3150,7 @@
     reference type @a ReferenceType fits to the JSON value; throws
     std::domain_error otherwise
 
-    @throw std::domain_error in case passed type @a ReferenceType is
+    @throw type_error (310) in case passed type @a ReferenceType is
     incompatible with the stored JSON value
 
     @complexity Constant.
@@ -3073,7 +3198,7 @@
 
     @return copy of the JSON value, converted to type @a ValueType
 
-    @throw std::domain_error in case passed type @a ValueType is incompatible
+    @throw type_error (310) in case passed type @a ValueType is incompatible
     to JSON, thrown by @ref get() const
 
     @complexity Linear in the size of the JSON value.
@@ -3122,7 +3247,7 @@
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if the JSON value is not an array; example:
+    @throw type_error (303) 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()`; example: `"array index 7 is out of range"`
@@ -3152,7 +3277,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3166,7 +3291,7 @@
 
     @return const reference to the element at index @a idx
 
-    @throw std::domain_error if the JSON value is not an array; example:
+    @throw type_error (303) 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()`; example: `"array index 7 is out of range"`
@@ -3196,7 +3321,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3210,7 +3335,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if the JSON value is not an object; example:
+    @throw type_error (303) 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()`; example: `"key "the fast" not found"`
@@ -3244,7 +3369,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3258,7 +3383,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if the JSON value is not an object; example:
+    @throw type_error (303) 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()`; example: `"key "the fast" not found"`
@@ -3292,7 +3417,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use at() with " + type_name());
+            throw type_error(303, "cannot use at() with " + type_name());
         }
     }
 
@@ -3309,7 +3434,7 @@
 
     @return reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array or null; example:
+    @throw type_error (304) if JSON is not an array or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Constant if @a idx is in the range of the array. Otherwise
@@ -3346,7 +3471,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3359,7 +3484,7 @@
 
     @return const reference to the element at index @a idx
 
-    @throw std::domain_error if JSON is not an array; example: `"cannot use
+    @throw type_error (304) if JSON is not an array; example: `"cannot use
     operator[] with null"`
 
     @complexity Constant.
@@ -3379,7 +3504,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3396,7 +3521,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object or null; example:
+    @throw type_error (304) if JSON is not an object or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Logarithmic in the size of the container.
@@ -3427,7 +3552,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3444,7 +3569,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (304) if JSON is not an object; example: `"cannot use
     operator[] with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3469,7 +3594,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3486,7 +3611,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object or null; example:
+    @throw type_error (304) if JSON is not an object or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Logarithmic in the size of the container.
@@ -3521,7 +3646,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (304) if JSON is not an object; example: `"cannot use
     operator[] with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3554,7 +3679,7 @@
 
     @return reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object or null; example:
+    @throw type_error (304) if JSON is not an object or null; example:
     `"cannot use operator[] with string"`
 
     @complexity Logarithmic in the size of the container.
@@ -3586,7 +3711,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3603,7 +3728,7 @@
 
     @return const reference to the element at key @a key
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (304) if JSON is not an object; example: `"cannot use
     operator[] with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3629,7 +3754,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use operator[] with " + type_name());
+            throw type_error(304, "cannot use operator[] with " + type_name());
         }
     }
 
@@ -3666,7 +3791,7 @@
     @return copy of the element at key @a key or @a default_value if @a key
     is not found
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (305) if JSON is not an object; example: `"cannot use
     value() with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3703,7 +3828,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use value() with " + type_name());
+            throw type_error(305, "cannot use value() with " + type_name());
         }
     }
 
@@ -3745,7 +3870,7 @@
     @return copy of the element at key @a key or @a default_value if @a key
     is not found
 
-    @throw std::domain_error if JSON is not an object; example: `"cannot use
+    @throw type_error (305) if JSON is not an object; example: `"cannot use
     value() with null"`
 
     @complexity Logarithmic in the size of the container.
@@ -3778,7 +3903,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use value() with " + type_name());
+            throw type_error(305, "cannot use value() with " + type_name());
         }
     }
 
@@ -3807,7 +3932,7 @@
     or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
-    @throw std::out_of_range when called on `null` value
+    @throw invalid_iterator (114) when called on `null` value
 
     @liveexample{The following code shows an example for `front()`.,front}
 
@@ -3849,7 +3974,7 @@
     or an empty array or object (undefined behavior, guarded by assertions).
     @post The JSON value remains unchanged.
 
-    @throw std::out_of_range when called on `null` value.
+    @throw invalid_iterator (114) when called on `null` value
 
     @liveexample{The following code shows an example for `back()`.,back}
 
@@ -3893,11 +4018,12 @@
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot
+    @throw type_error (306) if called on a `null` value; example: `"cannot
     use erase() with null"`
-    @throw std::domain_error if called on an iterator which does not belong to
-    the current JSON value; example: `"iterator does not fit current value"`
-    @throw std::out_of_range if called on a primitive type with invalid
+    @throw invalid_iterator (103) if called on an iterator which does not belong
+    to the current JSON value; example: `"iterator does not fit current
+    value"`
+    @throw invalid_iterator (105) if called on a primitive type with invalid
     iterator (i.e., any iterator which is not `begin()`); example: `"iterator
     out of range"`
 
@@ -3930,7 +4056,7 @@
         // make sure iterator fits the current value
         if (this != pos.m_object)
         {
-            throw std::domain_error("iterator does not fit current value");
+            throw invalid_iterator(103, "iterator does not fit current value");
         }
 
         InteratorType result = end();
@@ -3945,7 +4071,7 @@
             {
                 if (not pos.m_it.primitive_iterator.is_begin())
                 {
-                    throw std::out_of_range("iterator out of range");
+                    throw invalid_iterator(105, "iterator out of range");
                 }
 
                 if (is_string())
@@ -3974,7 +4100,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                throw type_error(306, "cannot use erase() with " + type_name());
             }
         }
 
@@ -4001,11 +4127,11 @@
     @post Invalidates iterators and references at or after the point of the
     erase, including the `end()` iterator.
 
-    @throw std::domain_error if called on a `null` value; example: `"cannot
+    @throw type_error (306) if called on a `null` value; example: `"cannot
     use erase() with null"`
-    @throw std::domain_error if called on iterators which does not belong to
-    the current JSON value; example: `"iterators do not fit current value"`
-    @throw std::out_of_range if called on a primitive type with invalid
+    @throw invalid_iterator (102) if called on iterators which does not belong
+    to the current JSON value; example: `"iterators do not fit current value"`
+    @throw invalid_iterator (104) if called on a primitive type with invalid
     iterators (i.e., if `first != begin()` and `last != end()`); example:
     `"iterators out of range"`
 
@@ -4038,7 +4164,7 @@
         // make sure iterator fits the current value
         if (this != first.m_object or this != last.m_object)
         {
-            throw std::domain_error("iterators do not fit current value");
+            throw invalid_iterator(102, "iterators do not fit current value");
         }
 
         InteratorType result = end();
@@ -4053,7 +4179,7 @@
             {
                 if (not first.m_it.primitive_iterator.is_begin() or not last.m_it.primitive_iterator.is_end())
                 {
-                    throw std::out_of_range("iterators out of range");
+                    throw invalid_iterator(104, "iterators out of range");
                 }
 
                 if (is_string())
@@ -4084,7 +4210,7 @@
 
             default:
             {
-                throw std::domain_error("cannot use erase() with " + type_name());
+                throw type_error(306, "cannot use erase() with " + type_name());
             }
         }
 
@@ -4105,7 +4231,7 @@
     @post References and iterators to the erased elements are invalidated.
     Other references and iterators are not affected.
 
-    @throw std::domain_error when called on a type other than JSON object;
+    @throw type_error (306) when called on a type other than JSON object;
     example: `"cannot use erase() with null"`
 
     @complexity `log(size()) + count(key)`
@@ -4130,7 +4256,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use erase() with " + type_name());
+            throw type_error(306, "cannot use erase() with " + type_name());
         }
     }
 
@@ -4141,7 +4267,7 @@
 
     @param[in] idx index of the element to remove
 
-    @throw std::domain_error when called on a type other than JSON array;
+    @throw type_error (306) when called on a type other than JSON array;
     example: `"cannot use erase() with null"`
     @throw std::out_of_range when `idx >= size()`; example: `"array index 17
     is out of range"`
@@ -4173,7 +4299,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use erase() with " + type_name());
+            throw type_error(306, "cannot use erase() with " + type_name());
         }
     }
 
@@ -4883,7 +5009,7 @@
 
     @param[in] val the value to add to the JSON array
 
-    @throw std::domain_error when called on a type other than JSON array or
+    @throw type_error (307) when called on a type other than JSON array or
     null; example: `"cannot use push_back() with number"`
 
     @complexity Amortized constant.
@@ -4899,7 +5025,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            throw type_error(307, "cannot use push_back() with " + type_name());
         }
 
         // transform null object into an array
@@ -4935,7 +5061,7 @@
         // push_back only works for null objects or arrays
         if (not(is_null() or is_array()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            throw type_error(307, "cannot use push_back() with " + type_name());
         }
 
         // transform null object into an array
@@ -4969,7 +5095,7 @@
 
     @param[in] val the value to add to the JSON object
 
-    @throw std::domain_error when called on a type other than JSON object or
+    @throw type_error (307) when called on a type other than JSON object or
     null; example: `"cannot use push_back() with number"`
 
     @complexity Logarithmic in the size of the container, O(log(`size()`)).
@@ -4985,7 +5111,7 @@
         // push_back only works for null objects or objects
         if (not(is_null() or is_object()))
         {
-            throw std::domain_error("cannot use push_back() with " + type_name());
+            throw type_error(307, "cannot use push_back() with " + type_name());
         }
 
         // transform null object into an object
@@ -5068,10 +5194,10 @@
     @param[in] val element to insert
     @return iterator pointing to the inserted @a val.
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
 
     @complexity Constant plus linear in the distance between pos and end of the
     container.
@@ -5088,7 +5214,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                throw invalid_iterator(103, "iterator does not fit current value");
             }
 
             // insert to array and return iterator
@@ -5099,7 +5225,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
     }
 
@@ -5124,10 +5250,10 @@
     @return iterator pointing to the first element inserted, or @a pos if
     `cnt==0`
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
 
     @complexity Linear in @a cnt plus linear in the distance between @a pos
     and end of the container.
@@ -5144,7 +5270,7 @@
             // check if iterator pos fits to this JSON value
             if (pos.m_object != this)
             {
-                throw std::domain_error("iterator does not fit current value");
+                throw invalid_iterator(103, "iterator does not fit current value");
             }
 
             // insert to array and return iterator
@@ -5155,7 +5281,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
     }
 
@@ -5169,13 +5295,13 @@
     @param[in] first begin of the range of elements to insert
     @param[in] last end of the range of elements to insert
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
-    @throw std::domain_error if @a first and @a last do not belong to the same
-    JSON value; example: `"iterators do not fit"`
-    @throw std::domain_error if @a first or @a last are iterators into
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
+    @throw invalid_iterator (110) if @a first and @a last do not belong to the
+    same JSON value; example: `"iterators do not fit"`
+    @throw invalid_iterator (111) if @a first or @a last are iterators into
     container for which insert is called; example: `"passed iterators may not
     belong to container"`
 
@@ -5194,24 +5320,25 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            throw invalid_iterator(103, "iterator does not fit current value");
         }
 
         // check if range iterators belong to the same JSON object
         if (first.m_object != last.m_object)
         {
-            throw std::domain_error("iterators do not fit");
+            throw invalid_iterator(110, "iterators do not fit");
         }
 
+        // first and last iterator must not belong to this container
         if (first.m_object == this or last.m_object == this)
         {
-            throw std::domain_error("passed iterators may not belong to container");
+            throw invalid_iterator(111, "passed iterators may not belong to container");
         }
 
         // insert to array and return iterator
@@ -5233,10 +5360,10 @@
     the end() iterator
     @param[in] ilist initializer list to insert the values from
 
-    @throw std::domain_error if called on JSON values other than arrays;
+    @throw type_error (309) if called on JSON values other than arrays;
     example: `"cannot use insert() with string"`
-    @throw std::domain_error if @a pos is not an iterator of *this; example:
-    `"iterator does not fit current value"`
+    @throw invalid_iterator (103) if @a pos is not an iterator of *this;
+    example: `"iterator does not fit current value"`
 
     @return iterator pointing to the first element inserted, or @a pos if
     `ilist` is empty
@@ -5253,13 +5380,13 @@
         // insert only works for arrays
         if (not is_array())
         {
-            throw std::domain_error("cannot use insert() with " + type_name());
+            throw type_error(309, "cannot use insert() with " + type_name());
         }
 
         // check if iterator pos fits to this JSON value
         if (pos.m_object != this)
         {
-            throw std::domain_error("iterator does not fit current value");
+            throw invalid_iterator(103, "iterator does not fit current value");
         }
 
         // insert to array and return iterator
@@ -5307,8 +5434,8 @@
 
     @param[in,out] other array to exchange the contents with
 
-    @throw std::domain_error when JSON value is not an array; example: `"cannot
-    use swap() with string"`
+    @throw type_error (308) when JSON value is not an array; example:
+    `"cannot use swap() with string"`
 
     @complexity Constant.
 
@@ -5327,7 +5454,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            throw type_error(308, "cannot use swap() with " + type_name());
         }
     }
 
@@ -5341,7 +5468,7 @@
 
     @param[in,out] other object to exchange the contents with
 
-    @throw std::domain_error when JSON value is not an object; example:
+    @throw type_error (308) when JSON value is not an object; example:
     `"cannot use swap() with string"`
 
     @complexity Constant.
@@ -5361,7 +5488,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            throw type_error(308, "cannot use swap() with " + type_name());
         }
     }
 
@@ -5375,7 +5502,7 @@
 
     @param[in,out] other string to exchange the contents with
 
-    @throw std::domain_error when JSON value is not a string; example: `"cannot
+    @throw type_error (308) when JSON value is not a string; example: `"cannot
     use swap() with boolean"`
 
     @complexity Constant.
@@ -5395,7 +5522,7 @@
         }
         else
         {
-            throw std::domain_error("cannot use swap() with " + type_name());
+            throw type_error(308, "cannot use swap() with " + type_name());
         }
     }
 
@@ -5899,6 +6026,8 @@
 
     @return result of the deserialization
 
+    @throw parse_error (2xx) in case of parse errors
+
     @complexity Linear in the length of the input. The parser is a predictive
     LL(1) parser. The complexity can be higher if the parser callback function
     @a cb has a super-linear complexity.
@@ -5927,6 +6056,8 @@
     which is used to control the deserialization by filtering unwanted values
     (optional)
 
+    @throw parse_error (2xx) in case of parse errors
+
     @return result of the deserialization
 
     @complexity Linear in the length of the input. The parser is a predictive
@@ -5966,7 +6097,7 @@
     @param[in,out] i  input stream to read a serialized JSON value from
     @param[in,out] j  JSON value to write the deserialized input to
 
-    @throw std::invalid_argument in case of parse errors
+    @throw parse_error (2xx) in case of parse errors
 
     @complexity Linear in the length of the input. The parser is a predictive
     LL(1) parser.
@@ -6752,7 +6883,7 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    throw invalid_iterator(114, "cannot get value");
                 }
 
                 default:
@@ -6763,7 +6894,7 @@
                     }
                     else
                     {
-                        throw std::out_of_range("cannot get value");
+                        throw invalid_iterator(114, "cannot get value");
                     }
                 }
             }
@@ -6798,7 +6929,7 @@
                     }
                     else
                     {
-                        throw std::out_of_range("cannot get value");
+                        throw invalid_iterator(114, "cannot get value");
                     }
                 }
             }
@@ -6884,7 +7015,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                throw invalid_iterator(112, "cannot compare iterators of different containers");
             }
 
             assert(m_object != nullptr);
@@ -6920,7 +7051,7 @@
             // if objects are not the same, the comparison is undefined
             if (m_object != other.m_object)
             {
-                throw std::domain_error("cannot compare iterators of different containers");
+                throw invalid_iterator(112, "cannot compare iterators of different containers");
             }
 
             assert(m_object != nullptr);
@@ -6929,7 +7060,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot compare order of object iterators");
+                    throw invalid_iterator(113, "cannot compare order of object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -6971,7 +7102,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    throw invalid_iterator(109, "cannot use offsets with object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -7021,7 +7152,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use offsets with object iterators");
+                    throw invalid_iterator(109, "cannot use offsets with object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -7045,7 +7176,7 @@
             {
                 case basic_json::value_t::object:
                 {
-                    throw std::domain_error("cannot use operator[] for object iterators");
+                    throw invalid_iterator(108, "cannot use operator[] for object iterators");
                 }
 
                 case basic_json::value_t::array:
@@ -7055,7 +7186,7 @@
 
                 case basic_json::value_t::null:
                 {
-                    throw std::out_of_range("cannot get value");
+                    throw invalid_iterator(114, "cannot get value");
                 }
 
                 default:
@@ -7066,7 +7197,7 @@
                     }
                     else
                     {
-                        throw std::out_of_range("cannot get value");
+                        throw invalid_iterator(114, "cannot get value");
                     }
                 }
             }
@@ -7083,7 +7214,7 @@
             }
             else
             {
-                throw std::domain_error("cannot use key() for non-object iterators");
+                throw invalid_iterator(107, "cannot use key() for non-object iterators");
             }
         }
 
@@ -7391,7 +7522,7 @@
         {
             m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
-            m_start = m_cursor = m_content;
+            m_buffer_start = m_start = m_cursor = m_content;
             m_limit = m_content + s.size();
         }
 
@@ -7403,7 +7534,7 @@
             std::getline(*m_stream, m_buffer);
             m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
-            m_start = m_cursor = m_content;
+            m_buffer_start = m_start = m_cursor = m_content;
             m_limit = m_content + m_buffer.size();
         }
 
@@ -7428,17 +7559,17 @@
         @return string representation of the code point; the length of the
         result string is between 1 and 4 characters.
 
-        @throw std::out_of_range if code point is > 0x10ffff; example: `"code
+        @throw parse_error (203) if code point is > 0x10ffff; example: `"code
         points above 0x10FFFF are invalid"`
-        @throw std::invalid_argument if the low surrogate is invalid; example:
-        `""missing or wrong low surrogate""`
+        @throw parse_error (202) if the low surrogate is invalid; example:
+        `"missing or wrong low surrogate"`
 
         @complexity Constant.
 
         @see <http://en.wikipedia.org/wiki/UTF-8#Sample_code>
         */
-        static string_t to_unicode(const std::size_t codepoint1,
-                                   const std::size_t codepoint2 = 0)
+        string_t to_unicode(const std::size_t codepoint1,
+                            const std::size_t codepoint2 = 0) const
         {
             // calculate the code point from the given code points
             std::size_t codepoint = codepoint1;
@@ -7461,7 +7592,8 @@
                 }
                 else
                 {
-                    throw std::invalid_argument("missing or wrong low surrogate");
+                    const std::string error_msg = error_message() + "; missing or wrong low surrogate";
+                    throw parse_error(202, error_msg);
                 }
             }
 
@@ -7495,7 +7627,8 @@
             }
             else
             {
-                throw std::out_of_range("code points above 0x10FFFF are invalid");
+                const std::string error_msg = error_message() + "; code points above 0x10FFFF are invalid";
+                throw parse_error(203, error_msg);
             }
 
             return result;
@@ -7645,7 +7778,8 @@
         /// append data from the stream to the internal buffer
         void yyfill() noexcept
         {
-            if (m_stream == nullptr or not * m_stream)
+            // yyfill has no effect if we do not have a ready stream
+            if (m_stream == nullptr or m_stream->fail())
             {
                 return;
             }
@@ -7654,20 +7788,56 @@
             const auto offset_marker = m_marker - m_start;
             const auto offset_cursor = m_cursor - m_start;
 
-            m_buffer.erase(0, static_cast<size_t>(offset_start));
+            // get the size of the previous buffer
+            const auto last_buffer_size = static_cast<size_t>(offset_start);
+            // remmeber all buffer sizes so far
+            m_previous_buffer_sizes += last_buffer_size;
+
+            // delete previous buffer
+            m_buffer.erase(0, last_buffer_size);
+
+            // read line from stream to buffer
             std::string line;
             assert(m_stream != nullptr);
             std::getline(*m_stream, line);
             m_buffer += "\n" + line; // add line with newline symbol
 
+            // set the pointers accordingly
             m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
             assert(m_content != nullptr);
-            m_start  = m_content;
+            m_buffer_start = m_start = m_content;
             m_marker = m_start + offset_marker;
             m_cursor = m_start + offset_cursor;
             m_limit  = m_start + m_buffer.size() - 1;
         }
 
+        /// return the location of the end of the last token
+        size_t last_token_location() const noexcept
+        {
+            // m_buffer_start points to the first byte of the current buffer
+            // m_cursor points to the byte behind the current token
+            // m_previous_buffer_sizes contains the size the previous buffers
+            return static_cast<size_t>(m_cursor - m_buffer_start) + m_previous_buffer_sizes;
+        }
+
+        /// returns an error message for parse errors
+        std::string error_message() const
+        {
+            const std::string last_token(reinterpret_cast<typename std::string::const_pointer>(m_start),
+                                         static_cast<size_t>(m_cursor - m_start));
+            std::string result = "parse error: unexpected ";
+            if (last_token_type == token_type::end_of_input)
+            {
+                result += token_type_name(token_type::end_of_input);
+            }
+            else
+            {
+                result += "'" + last_token + "'";
+            }
+            result += " at byte " + std::to_string(last_token_location() - last_token.size() + 1);
+            return result;
+        }
+
         /// return string representation of last read token
         string_t get_token_string() const
         {
@@ -7731,7 +7901,9 @@
 
         @return string value of current token without opening and closing
         quotes
-        @throw std::out_of_range if to_unicode fails
+
+        @throw parse_error (202,203) if to_unicode fails
+        @throw parse_error (204) if low surrogate is missing
         */
         string_t get_string() const
         {
@@ -7806,7 +7978,8 @@
                                 // make sure there is a subsequent unicode
                                 if ((i + 6 >= m_limit) or * (i + 5) != '\\' or * (i + 6) != 'u')
                                 {
-                                    throw std::invalid_argument("missing low surrogate");
+                                    std::string error_msg = error_message() + "; missing low surrogate";
+                                    throw parse_error(204, error_msg);
                                 }
 
                                 // get code yyyy from uxxxx\uyyyy
@@ -8019,6 +8192,10 @@
         const lexer_char_t* m_cursor = nullptr;
         /// pointer to the end of the buffer
         const lexer_char_t* m_limit = nullptr;
+        /// pointer to the beginning of the buffer
+        const lexer_char_t* m_buffer_start = nullptr;
+        /// the size of the last buffer (before yyfill was called)
+        size_t m_previous_buffer_sizes = 0;
         /// the last token type
         token_type last_token_type = token_type::end_of_input;
     };
@@ -8263,12 +8440,9 @@
         {
             if (t != last_token)
             {
-                std::string error_msg = "parse error - unexpected ";
-                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
-                              "'") :
-                              lexer::token_type_name(last_token));
+                std::string error_msg = m_lexer.error_message();
                 error_msg += "; expected " + lexer::token_type_name(t);
-                throw std::invalid_argument(error_msg);
+                throw parse_error(201, error_msg);
             }
         }
 
@@ -8276,11 +8450,9 @@
         {
             if (t == last_token)
             {
-                std::string error_msg = "parse error - unexpected ";
-                error_msg += (last_token == lexer::token_type::parse_error ? ("'" +  m_lexer.get_token_string() +
-                              "'") :
-                              lexer::token_type_name(last_token));
-                throw std::invalid_argument(error_msg);
+                std::string error_msg = m_lexer.error_message();
+                error_msg += "; expected JSON value";
+                throw parse_error(201, error_msg);
             }
         }
 
@@ -8323,10 +8495,10 @@
                       empty string is assumed which references the whole JSON
                       value
 
-        @throw std::domain_error if reference token is nonempty and does not
-        begin with a slash (`/`); example: `"JSON pointer must be empty or
+        @throw extension_error (403) if reference token is nonempty and does
+        not begin with a slash (`/`); example: `"JSON pointer must be empty or
         begin with /"`
-        @throw std::domain_error if a tilde (`~`) is not followed by `0`
+        @throw extension_error (404) if a tilde (`~`) is not followed by `0`
         (representing `~`) or `1` (representing `/`); example: `"escape error:
         ~ must be followed with 0 or 1"`
 
@@ -8376,7 +8548,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                throw extension_error(401, "JSON pointer has no parent");
             }
 
             auto last = reference_tokens.back();
@@ -8394,7 +8566,7 @@
         {
             if (is_root())
             {
-                throw std::domain_error("JSON pointer has no parent");
+                throw extension_error(401, "JSON pointer has no parent");
             }
 
             json_pointer result = *this;
@@ -8455,7 +8627,7 @@
                     */
                     default:
                     {
-                        throw std::domain_error("invalid value to unflatten");
+                        throw extension_error(411, "invalid value to unflatten");
                     }
                 }
             }
@@ -8473,8 +8645,10 @@
         @complexity Linear in the length of the JSON pointer.
 
         @throw std::out_of_range      if the JSON pointer can not be resolved
-        @throw std::domain_error      if an array index begins with '0'
-        @throw std::invalid_argument  if an array index was not a number
+        @throw extension_error (402)  if an array index begins with '0' or is
+        not a number
+        @throw extension_error (405)  if the reference token could not be
+        resolved
         */
         reference get_unchecked(pointer ptr) const
         {
@@ -8494,7 +8668,7 @@
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         if (reference_token == "-")
@@ -8505,14 +8679,24 @@
                         else
                         {
                             // convert array index to number; unchecked access
-                            ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                            try
+                            {
+                                ptr = &ptr->operator[](static_cast<size_type>(std::stoi(reference_token)));
+                            }
+                            catch (std::invalid_argument&)
+                            {
+                                // std::stoi throws std::invalid_argument if
+                                // no conversion could be performed; we catch
+                                // it for better diagnosis
+                                throw extension_error(402, "array index is '" + reference_token + "', but must be a number");
+                            }
                         }
                         break;
                     }
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -8538,15 +8722,15 @@
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            throw std::out_of_range("array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range");
+                            throw extension_error(406, "array index '-' (" +
+                                                  std::to_string(ptr->m_value.array->size()) +
+                                                  ") is out of range");
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         // note: at performs range check
@@ -8556,7 +8740,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -8590,15 +8774,15 @@
                         if (reference_token == "-")
                         {
                             // "-" cannot be used for const access
-                            throw std::out_of_range("array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range");
+                            throw extension_error(406, "array index '-' (" +
+                                                  std::to_string(ptr->m_value.array->size()) +
+                                                  ") is out of range");
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         // use unchecked array access
@@ -8608,7 +8792,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -8634,15 +8818,15 @@
                         if (reference_token == "-")
                         {
                             // "-" always fails the range check
-                            throw std::out_of_range("array index '-' (" +
-                                                    std::to_string(ptr->m_value.array->size()) +
-                                                    ") is out of range");
+                            throw extension_error(406, "array index '-' (" +
+                                                  std::to_string(ptr->m_value.array->size()) +
+                                                  ") is out of range");
                         }
 
                         // error condition (cf. RFC 6901, Sect. 4)
                         if (reference_token.size() > 1 and reference_token[0] == '0')
                         {
-                            throw std::domain_error("array index must not begin with '0'");
+                            throw extension_error(402, "array index must not begin with '0'");
                         }
 
                         // note: at performs range check
@@ -8652,7 +8836,7 @@
 
                     default:
                     {
-                        throw std::out_of_range("unresolved reference token '" + reference_token + "'");
+                        throw extension_error(405, "unresolved reference token '" + reference_token + "'");
                     }
                 }
             }
@@ -8674,7 +8858,7 @@
             // check if nonempty reference string begins with slash
             if (reference_string[0] != '/')
             {
-                throw std::domain_error("JSON pointer must be empty or begin with '/'");
+                throw extension_error(403, "JSON pointer must be empty or begin with '/'");
             }
 
             // extract the reference tokens:
@@ -8709,13 +8893,13 @@
                             (reference_token[pos + 1] != '0' and
                              reference_token[pos + 1] != '1'))
                     {
-                        throw std::domain_error("escape error: '~' must be followed with '0' or '1'");
+                        throw extension_error(404, "escape error: '~' must be followed with '0' or '1'");
                     }
                 }
 
                 // finally, store the reference token
                 unescape(reference_token);
-                result.push_back(reference_token);
+                result.emplace_back(reference_token);
             }
 
             return result;
@@ -8837,7 +9021,7 @@
         {
             if (not value.is_object())
             {
-                throw std::domain_error("only objects can be unflattened");
+                throw extension_error(412, "only objects can be unflattened");
             }
 
             basic_json result;
@@ -8848,7 +9032,7 @@
             {
                 if (not element.second.is_primitive())
                 {
-                    throw std::domain_error("values in object must be primitive");
+                    throw extension_error(413, "values in object must be primitive");
                 }
 
                 // assign value to reference pointed to by JSON pointer; Note
@@ -8900,8 +9084,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,operatorjson_pointer}
 
@@ -8927,8 +9111,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,operatorjson_pointer_const}
 
@@ -8952,8 +9136,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,at_json_pointer}
 
@@ -8977,8 +9161,8 @@
     @complexity Constant.
 
     @throw std::out_of_range      if the JSON pointer can not be resolved
-    @throw std::domain_error      if an array index begins with '0'
-    @throw std::invalid_argument  if an array index was not a number
+    @throw extension_error (402)  if an array index begins with '0' or is not
+    a number
 
     @liveexample{The behavior is shown in the example.,at_json_pointer_const}
 
@@ -9078,8 +9262,11 @@
     @throw std::out_of_range if a JSON pointer inside the patch could not
     be resolved successfully in the current JSON value; example: `"key baz
     not found"`
-    @throw invalid_argument if the JSON patch is malformed (e.g., mandatory
-    attributes are missing); example: `"operation add must have member path"`
+    @throw extension_error (408) if the JSON patch is malformed (e.g.,
+    mandatory attributes are missing); example: `"operation add must have
+    string member path"`
+    @throw extension_error (407) if the JSON patch is malformed; example:
+    `"JSON patch must be an array of objects"`
 
     @complexity Linear in the size of the JSON value and the length of the
     JSON patch. As usually only a fraction of the JSON value is affected by
@@ -9177,7 +9364,7 @@
                             if (static_cast<size_type>(idx) > parent.size())
                             {
                                 // avoid undefined behavior
-                                throw std::out_of_range("array index " + std::to_string(idx) + " is out of range");
+                                throw extension_error(406, "array index " + std::to_string(idx) + " is out of range");
                             }
                             else
                             {
@@ -9229,7 +9416,7 @@
         if (not json_patch.is_array())
         {
             // a JSON patch must be an array of objects
-            throw std::invalid_argument("JSON patch must be an array of objects");
+            throw extension_error(407, "JSON patch must be an array of objects");
         }
 
         // iterate and apply th eoperations
@@ -9249,13 +9436,13 @@
                 // check if desired value is present
                 if (it == val.m_value.object->end())
                 {
-                    throw std::invalid_argument(error_msg + " must have member '" + member + "'");
+                    throw extension_error(408, error_msg + " must have string member '" + member + "'");
                 }
 
                 // check if result is of type string
                 if (string_type and not it->second.is_string())
                 {
-                    throw std::invalid_argument(error_msg + " must have string member '" + member + "'");
+                    throw extension_error(408, error_msg + " must have string member '" + member + "'");
                 }
 
                 // no error: return value
@@ -9265,7 +9452,7 @@
             // type check
             if (not val.is_object())
             {
-                throw std::invalid_argument("JSON patch must be an array of objects");
+                throw extension_error(407, "JSON patch must be an array of objects");
             }
 
             // collect mandatory members
@@ -9338,7 +9525,7 @@
                     // throw an exception if test fails
                     if (not success)
                     {
-                        throw std::domain_error("unsuccessful: " + val.dump());
+                        throw extension_error(410, "unsuccessful: " + val.dump());
                     }
 
                     break;
@@ -9348,7 +9535,7 @@
                 {
                     // op must be "add", "remove", "replace", "move", "copy", or
                     // "test"
-                    throw std::invalid_argument("operation value '" + op + "' is invalid");
+                    throw extension_error(409, "operation value '" + op + "' is invalid");
                 }
             }
         }
diff --git a/test/data/json.org/1.json b/test/data/json.org/1.json
index eacfbf5..c8f1d8c 100644
--- a/test/data/json.org/1.json
+++ b/test/data/json.org/1.json
@@ -1,20 +1,20 @@
 {
     "glossary": {
         "title": "example glossary",
-		"GlossDiv": {
+        "GlossDiv": {
             "title": "S",
-			"GlossList": {
+            "GlossList": {
                 "GlossEntry": {
                     "ID": "SGML",
-					"SortAs": "SGML",
-					"GlossTerm": "Standard Generalized Markup Language",
-					"Acronym": "SGML",
-					"Abbrev": "ISO 8879:1986",
-					"GlossDef": {
+                    "SortAs": "SGML",
+                    "GlossTerm": "Standard Generalized Markup Language",
+                    "Acronym": "SGML",
+                    "Abbrev": "ISO 8879:1986",
+                    "GlossDef": {
                         "para": "A meta-markup language, used to create markup languages such as DocBook.",
-						"GlossSeeAlso": ["GML", "XML"]
+                        "GlossSeeAlso": ["GML", "XML"]
                     },
-					"GlossSee": "markup"
+                    "GlossSee": "markup"
                 }
             }
         }
diff --git a/test/src/fuzz.cpp b/test/src/fuzz.cpp
index d8b7ef7..c203845 100644
--- a/test/src/fuzz.cpp
+++ b/test/src/fuzz.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (fuzz test support)
-|  |  |__   |  |  | | | |  version 2.0.0
+|  |  |__   |  |  | | | |  version 3.0.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Run "make fuzz_testing" and follow the instructions.
@@ -24,7 +24,7 @@
             json j(std::cin);
             std::cout << j << std::endl;
         }
-        catch (std::invalid_argument& e)
+        catch (json::parse_error& e)
         {
             std::cout << "Invalid argument in parsing" << e.what() << '\n';
         }
diff --git a/test/src/unit.cpp b/test/src/unit.cpp
index edbafac..b7688a4 100644
--- a/test/src/unit.cpp
+++ b/test/src/unit.cpp
@@ -1,7 +1,7 @@
 /*
     __ _____ _____ _____
  __|  |   __|     |   | |  JSON for Modern C++ (test suite)
-|  |  |__   |  |  | | | |  version 2.0.1
+|  |  |__   |  |  | | | |  version 3.0.0
 |_____|_____|_____|_|___|  https://github.com/nlohmann/json
 
 Licensed under the MIT License <http://opensource.org/licenses/MIT>.
@@ -944,9 +944,9 @@
             SECTION("object with error")
             {
                 CHECK_THROWS_AS(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
-                std::logic_error);
+                json::type_error);
                 CHECK_THROWS_WITH(json::object({ {"one", 1}, {"two", 1u}, {"three", 2.2}, {"four", false}, 13 }),
-                "cannot create object from initializer list");
+                "[except.301] cannot create object from initializer list");
             }
 
             SECTION("empty array")
@@ -1018,18 +1018,22 @@
                 {
                     json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
                     json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-                    CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), std::domain_error);
-                    CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), std::domain_error);
-                    CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()), "iterators are not compatible");
-                    CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()), "iterators are not compatible");
+                    CHECK_THROWS_AS(json(jobject.begin(), jobject2.end()), json::invalid_iterator);
+                    CHECK_THROWS_AS(json(jobject2.begin(), jobject.end()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(json(jobject.begin(), jobject2.end()),
+                                      "[except.101] iterators are not compatible");
+                    CHECK_THROWS_WITH(json(jobject2.begin(), jobject.end()),
+                                      "[except.101] iterators are not compatible");
                 }
                 {
                     json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
                     json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-                    CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), std::domain_error);
-                    CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), std::domain_error);
-                    CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()), "iterators are not compatible");
-                    CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()), "iterators are not compatible");
+                    CHECK_THROWS_AS(json(jobject.cbegin(), jobject2.cend()), json::invalid_iterator);
+                    CHECK_THROWS_AS(json(jobject2.cbegin(), jobject.cend()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(json(jobject.cbegin(), jobject2.cend()),
+                                      "[except.101] iterators are not compatible");
+                    CHECK_THROWS_WITH(json(jobject2.cbegin(), jobject.cend()),
+                                      "[except.101] iterators are not compatible");
                 }
             }
         }
@@ -1083,18 +1087,20 @@
                 {
                     json jarray = {1, 2, 3, 4};
                     json jarray2 = {2, 3, 4, 5};
-                    CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), std::domain_error);
-                    CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), std::domain_error);
-                    CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "iterators are not compatible");
-                    CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "iterators are not compatible");
+                    CHECK_THROWS_AS(json(jarray.begin(), jarray2.end()), json::invalid_iterator);
+                    CHECK_THROWS_AS(json(jarray2.begin(), jarray.end()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(json(jarray.begin(), jarray2.end()), "[except.101] iterators are not compatible");
+                    CHECK_THROWS_WITH(json(jarray2.begin(), jarray.end()), "[except.101] iterators are not compatible");
                 }
                 {
                     json jarray = {1, 2, 3, 4};
                     json jarray2 = {2, 3, 4, 5};
-                    CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), std::domain_error);
-                    CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), std::domain_error);
-                    CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()), "iterators are not compatible");
-                    CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()), "iterators are not compatible");
+                    CHECK_THROWS_AS(json(jarray.cbegin(), jarray2.cend()), json::invalid_iterator);
+                    CHECK_THROWS_AS(json(jarray2.cbegin(), jarray.cend()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(json(jarray.cbegin(), jarray2.cend()),
+                                      "[except.101] iterators are not compatible");
+                    CHECK_THROWS_WITH(json(jarray2.cbegin(), jarray.cend()),
+                                      "[except.101] iterators are not compatible");
                 }
             }
         }
@@ -1107,13 +1113,15 @@
                 {
                     {
                         json j;
-                        CHECK_THROWS_AS(json(j.begin(), j.end()), std::domain_error);
-                        CHECK_THROWS_WITH(json(j.begin(), j.end()), "cannot use construct with iterators from null");
+                        CHECK_THROWS_AS(json(j.begin(), j.end()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.begin(), j.end()),
+                                          "[except.106] cannot use construct with iterators from null");
                     }
                     {
                         json j;
-                        CHECK_THROWS_AS(json(j.cbegin(), j.cend()), std::domain_error);
-                        CHECK_THROWS_WITH(json(j.cbegin(), j.cend()), "cannot use construct with iterators from null");
+                        CHECK_THROWS_AS(json(j.cbegin(), j.cend()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.cbegin(), j.cend()),
+                                          "[except.106] cannot use construct with iterators from null");
                     }
                 }
 
@@ -1194,17 +1202,17 @@
                 {
                     {
                         json j = "foo";
-                        CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.end(), j.end()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[except.104] iterators out of range");
                     }
                     {
                         json j = "bar";
-                        CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                     }
                 }
 
@@ -1212,17 +1220,17 @@
                 {
                     {
                         json j = false;
-                        CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.end(), j.end()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[except.104] iterators out of range");
                     }
                     {
                         json j = true;
-                        CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                     }
                 }
 
@@ -1230,17 +1238,17 @@
                 {
                     {
                         json j = 17;
-                        CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.end(), j.end()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[except.104] iterators out of range");
                     }
                     {
                         json j = 17;
-                        CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                     }
                 }
 
@@ -1248,17 +1256,17 @@
                 {
                     {
                         json j = 17u;
-                        CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.end(), j.end()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[except.104] iterators out of range");
                     }
                     {
                         json j = 17u;
-                        CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                     }
                 }
 
@@ -1266,17 +1274,17 @@
                 {
                     {
                         json j = 23.42;
-                        CHECK_THROWS_AS(json(j.end(), j.end()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.begin(), j.begin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.end(), j.end()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.end(), j.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.begin(), j.begin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.end(), j.end()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.begin(), j.begin()), "[except.104] iterators out of range");
                     }
                     {
                         json j = 23.42;
-                        CHECK_THROWS_AS(json(j.cend(), j.cend()), std::out_of_range);
-                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), std::out_of_range);
-                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "iterators out of range");
-                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "iterators out of range");
+                        CHECK_THROWS_AS(json(j.cend(), j.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(json(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(json(j.cend(), j.cend()), "[except.104] iterators out of range");
+                        CHECK_THROWS_WITH(json(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                     }
                 }
             }
@@ -1491,6 +1499,7 @@
             CHECK(not j.is_number());
             CHECK(not j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(j.is_object());
             CHECK(not j.is_array());
@@ -1508,6 +1517,7 @@
             CHECK(not j.is_number());
             CHECK(not j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(not j.is_object());
             CHECK(j.is_array());
@@ -1525,6 +1535,7 @@
             CHECK(not j.is_number());
             CHECK(not j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(not j.is_object());
             CHECK(not j.is_array());
@@ -1542,6 +1553,7 @@
             CHECK(not j.is_number());
             CHECK(not j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(not j.is_object());
             CHECK(not j.is_array());
@@ -1559,6 +1571,7 @@
             CHECK(not j.is_number());
             CHECK(not j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(not j.is_object());
             CHECK(not j.is_array());
@@ -1576,6 +1589,7 @@
             CHECK(j.is_number());
             CHECK(j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(not j.is_object());
             CHECK(not j.is_array());
@@ -1593,6 +1607,7 @@
             CHECK(j.is_number());
             CHECK(j.is_number_integer());
             CHECK(j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(not j.is_object());
             CHECK(not j.is_array());
@@ -1610,6 +1625,7 @@
             CHECK(j.is_number());
             CHECK(not j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(j.is_number_float());
             CHECK(not j.is_object());
             CHECK(not j.is_array());
@@ -1627,6 +1643,7 @@
             CHECK(not j.is_number());
             CHECK(not j.is_number_integer());
             CHECK(not j.is_number_unsigned());
+            CHECK(not j.is_number_signed());
             CHECK(not j.is_number_float());
             CHECK(not j.is_object());
             CHECK(not j.is_array());
@@ -1860,28 +1877,28 @@
 
         SECTION("exception in case of a non-object type")
         {
-            CHECK_THROWS_AS(json(json::value_t::null).get<json::object_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::array).get<json::object_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::string).get<json::object_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::object_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::object_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::object_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::object_t>(), std::logic_error);
+            CHECK_THROWS_AS(json(json::value_t::null).get<json::object_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::array).get<json::object_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::string).get<json::object_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::object_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::object_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::object_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::object_t>(), json::type_error);
 
             CHECK_THROWS_WITH(json(json::value_t::null).get<json::object_t>(),
-                              "type must be object, but is null");
+                              "[except.302] type must be object, but is null");
             CHECK_THROWS_WITH(json(json::value_t::array).get<json::object_t>(),
-                              "type must be object, but is array");
+                              "[except.302] type must be object, but is array");
             CHECK_THROWS_WITH(json(json::value_t::string).get<json::object_t>(),
-                              "type must be object, but is string");
+                              "[except.302] type must be object, but is string");
             CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::object_t>(),
-                              "type must be object, but is boolean");
+                              "[except.302] type must be object, but is boolean");
             CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::object_t>(),
-                              "type must be object, but is number");
+                              "[except.302] type must be object, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::object_t>(),
-                              "type must be object, but is number");
+                              "[except.302] type must be object, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::object_t>(),
-                              "type must be object, but is number");
+                              "[except.302] type must be object, but is number");
         }
     }
 
@@ -1958,28 +1975,28 @@
 
         SECTION("exception in case of a non-array type")
         {
-            CHECK_THROWS_AS(json(json::value_t::null).get<json::array_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::object).get<json::array_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::string).get<json::array_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::array_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::array_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::array_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::array_t>(), std::logic_error);
+            CHECK_THROWS_AS(json(json::value_t::null).get<json::array_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::object).get<json::array_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::string).get<json::array_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::array_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::array_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::array_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::array_t>(), json::type_error);
 
             CHECK_THROWS_WITH(json(json::value_t::null).get<json::array_t>(),
-                              "type must be array, but is null");
+                              "[except.302] type must be array, but is null");
             CHECK_THROWS_WITH(json(json::value_t::object).get<json::array_t>(),
-                              "type must be array, but is object");
+                              "[except.302] type must be array, but is object");
             CHECK_THROWS_WITH(json(json::value_t::string).get<json::array_t>(),
-                              "type must be array, but is string");
+                              "[except.302] type must be array, but is string");
             CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::array_t>(),
-                              "type must be array, but is boolean");
+                              "[except.302] type must be array, but is boolean");
             CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::array_t>(),
-                              "type must be array, but is number");
+                              "[except.302] type must be array, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::array_t>(),
-                              "type must be array, but is number");
+                              "[except.302] type must be array, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::array_t>(),
-                              "type must be array, but is number");
+                              "[except.302] type must be array, but is number");
         }
     }
 
@@ -2038,28 +2055,28 @@
 
         SECTION("exception in case of a non-string type")
         {
-            CHECK_THROWS_AS(json(json::value_t::null).get<json::string_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::object).get<json::string_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::array).get<json::string_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::string_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::string_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::string_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::string_t>(), std::logic_error);
+            CHECK_THROWS_AS(json(json::value_t::null).get<json::string_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::object).get<json::string_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::array).get<json::string_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::string_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::string_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::string_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::string_t>(), json::type_error);
 
             CHECK_THROWS_WITH(json(json::value_t::null).get<json::string_t>(),
-                              "type must be string, but is null");
+                              "[except.302] type must be string, but is null");
             CHECK_THROWS_WITH(json(json::value_t::object).get<json::string_t>(),
-                              "type must be string, but is object");
+                              "[except.302] type must be string, but is object");
             CHECK_THROWS_WITH(json(json::value_t::array).get<json::string_t>(),
-                              "type must be string, but is array");
+                              "[except.302] type must be string, but is array");
             CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::string_t>(),
-                              "type must be string, but is boolean");
+                              "[except.302] type must be string, but is boolean");
             CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::string_t>(),
-                              "type must be string, but is number");
+                              "[except.302] type must be string, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::string_t>(),
-                              "type must be string, but is number");
+                              "[except.302] type must be string, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::string_t>(),
-                              "type must be string, but is number");
+                              "[except.302] type must be string, but is number");
         }
     }
 
@@ -2100,28 +2117,28 @@
 
         SECTION("exception in case of a non-string type")
         {
-            CHECK_THROWS_AS(json(json::value_t::null).get<json::boolean_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::object).get<json::boolean_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::array).get<json::boolean_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::string).get<json::boolean_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::boolean_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::boolean_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::boolean_t>(), std::logic_error);
+            CHECK_THROWS_AS(json(json::value_t::null).get<json::boolean_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::object).get<json::boolean_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::array).get<json::boolean_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::string).get<json::boolean_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_integer).get<json::boolean_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_unsigned).get<json::boolean_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::number_float).get<json::boolean_t>(), json::type_error);
 
             CHECK_THROWS_WITH(json(json::value_t::null).get<json::boolean_t>(),
-                              "type must be boolean, but is null");
+                              "[except.302] type must be boolean, but is null");
             CHECK_THROWS_WITH(json(json::value_t::object).get<json::boolean_t>(),
-                              "type must be boolean, but is object");
+                              "[except.302] type must be boolean, but is object");
             CHECK_THROWS_WITH(json(json::value_t::array).get<json::boolean_t>(),
-                              "type must be boolean, but is array");
+                              "[except.302] type must be boolean, but is array");
             CHECK_THROWS_WITH(json(json::value_t::string).get<json::boolean_t>(),
-                              "type must be boolean, but is string");
+                              "[except.302] type must be boolean, but is string");
             CHECK_THROWS_WITH(json(json::value_t::number_integer).get<json::boolean_t>(),
-                              "type must be boolean, but is number");
+                              "[except.302] type must be boolean, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_unsigned).get<json::boolean_t>(),
-                              "type must be boolean, but is number");
+                              "[except.302] type must be boolean, but is number");
             CHECK_THROWS_WITH(json(json::value_t::number_float).get<json::boolean_t>(),
-                              "type must be boolean, but is number");
+                              "[except.302] type must be boolean, but is number");
         }
     }
 
@@ -2356,22 +2373,22 @@
 
         SECTION("exception in case of a non-number type")
         {
-            CHECK_THROWS_AS(json(json::value_t::null).get<json::number_integer_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::object).get<json::number_integer_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::array).get<json::number_integer_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::string).get<json::number_integer_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::number_integer_t>(), std::logic_error);
+            CHECK_THROWS_AS(json(json::value_t::null).get<json::number_integer_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::object).get<json::number_integer_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::array).get<json::number_integer_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::string).get<json::number_integer_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::number_integer_t>(), json::type_error);
 
             CHECK_THROWS_WITH(json(json::value_t::null).get<json::number_integer_t>(),
-                              "type must be number, but is null");
+                              "[except.302] type must be number, but is null");
             CHECK_THROWS_WITH(json(json::value_t::object).get<json::number_integer_t>(),
-                              "type must be number, but is object");
+                              "[except.302] type must be number, but is object");
             CHECK_THROWS_WITH(json(json::value_t::array).get<json::number_integer_t>(),
-                              "type must be number, but is array");
+                              "[except.302] type must be number, but is array");
             CHECK_THROWS_WITH(json(json::value_t::string).get<json::number_integer_t>(),
-                              "type must be number, but is string");
+                              "[except.302] type must be number, but is string");
             CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::number_integer_t>(),
-                              "type must be number, but is boolean");
+                              "[except.302] type must be number, but is boolean");
 
             CHECK_NOTHROW(json(json::value_t::number_float).get<json::number_integer_t>());
             CHECK_NOTHROW(json(json::value_t::number_float).get<json::number_unsigned_t>());
@@ -2615,22 +2632,22 @@
 
         SECTION("exception in case of a non-string type")
         {
-            CHECK_THROWS_AS(json(json::value_t::null).get<json::number_float_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::object).get<json::number_float_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::array).get<json::number_float_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::string).get<json::number_float_t>(), std::logic_error);
-            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::number_float_t>(), std::logic_error);
+            CHECK_THROWS_AS(json(json::value_t::null).get<json::number_float_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::object).get<json::number_float_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::array).get<json::number_float_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::string).get<json::number_float_t>(), json::type_error);
+            CHECK_THROWS_AS(json(json::value_t::boolean).get<json::number_float_t>(), json::type_error);
 
             CHECK_THROWS_WITH(json(json::value_t::null).get<json::number_float_t>(),
-                              "type must be number, but is null");
+                              "[except.302] type must be number, but is null");
             CHECK_THROWS_WITH(json(json::value_t::object).get<json::number_float_t>(),
-                              "type must be number, but is object");
+                              "[except.302] type must be number, but is object");
             CHECK_THROWS_WITH(json(json::value_t::array).get<json::number_float_t>(),
-                              "type must be number, but is array");
+                              "[except.302] type must be number, but is array");
             CHECK_THROWS_WITH(json(json::value_t::string).get<json::number_float_t>(),
-                              "type must be number, but is string");
+                              "[except.302] type must be number, but is string");
             CHECK_THROWS_WITH(json(json::value_t::boolean).get<json::number_float_t>(),
-                              "type must be number, but is boolean");
+                              "[except.302] type must be number, but is boolean");
 
             CHECK_NOTHROW(json(json::value_t::number_integer).get<json::number_float_t>());
             CHECK_NOTHROW(json(json::value_t::number_unsigned).get<json::number_float_t>());
@@ -2712,8 +2729,9 @@
 
             SECTION("exception in case of a non-object type")
             {
-                CHECK_THROWS_AS((json().get<std::map<std::string, int>>()), std::logic_error);
-                CHECK_THROWS_WITH((json().get<std::map<std::string, int>>()), "type must be object, but is null");
+                CHECK_THROWS_AS((json().get<std::map<std::string, int>>()), json::type_error);
+                CHECK_THROWS_WITH((json().get<std::map<std::string, int>>()),
+                                  "[except.302] type must be object, but is null");
             }
         }
 
@@ -2781,15 +2799,19 @@
 
             SECTION("exception in case of a non-object type")
             {
-                CHECK_THROWS_AS((json().get<std::list<int>>()), std::logic_error);
-                CHECK_THROWS_AS((json().get<std::vector<int>>()), std::logic_error);
-                CHECK_THROWS_AS((json().get<std::vector<json>>()), std::logic_error);
-                CHECK_THROWS_AS((json().get<std::list<json>>()), std::logic_error);
+                CHECK_THROWS_AS((json().get<std::list<int>>()), json::type_error);
+                CHECK_THROWS_AS((json().get<std::vector<int>>()), json::type_error);
+                CHECK_THROWS_AS((json().get<std::vector<json>>()), json::type_error);
+                CHECK_THROWS_AS((json().get<std::list<json>>()), json::type_error);
 
-                CHECK_THROWS_WITH((json().get<std::list<int>>()), "type must be array, but is null");
-                CHECK_THROWS_WITH((json().get<std::vector<int>>()), "type must be array, but is null");
-                CHECK_THROWS_WITH((json().get<std::vector<json>>()), "type must be array, but is null");
-                CHECK_THROWS_WITH((json().get<std::list<json>>()), "type must be array, but is null");
+                CHECK_THROWS_WITH((json().get<std::list<int>>()),
+                                  "[except.302] type must be array, but is null");
+                CHECK_THROWS_WITH((json().get<std::vector<int>>()),
+                                  "[except.302] type must be array, but is null");
+                CHECK_THROWS_WITH((json().get<std::vector<json>>()),
+                                  "[except.302] type must be array, but is null");
+                CHECK_THROWS_WITH((json().get<std::list<json>>()),
+                                  "[except.302] type must be array, but is null");
             }
         }
     }
@@ -2992,7 +3014,7 @@
         CHECK(value.get_ptr<json::array_t*>() == nullptr);
         CHECK(value.get_ptr<json::string_t*>() == nullptr);
         CHECK(value.get_ptr<json::boolean_t*>() == nullptr);
-        CHECK(value.get_ptr<json::number_integer_t*>() != nullptr);
+        CHECK(value.get_ptr<json::number_integer_t*>() == nullptr);
         CHECK(value.get_ptr<json::number_unsigned_t*>() != nullptr);
         CHECK(value.get_ptr<json::number_float_t*>() == nullptr);
     }
@@ -3059,11 +3081,25 @@
 
         // check if mismatching references throw correctly
         CHECK_NOTHROW(value.get_ref<json::object_t&>());
-        CHECK_THROWS(value.get_ref<json::array_t&>());
-        CHECK_THROWS(value.get_ref<json::string_t&>());
-        CHECK_THROWS(value.get_ref<json::boolean_t&>());
-        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
-        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::array_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::string_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::boolean_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_integer_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_unsigned_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_float_t&>(), json::type_error);
+
+        CHECK_THROWS_WITH(value.get_ref<json::array_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is object");
+        CHECK_THROWS_WITH(value.get_ref<json::string_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is object");
+        CHECK_THROWS_WITH(value.get_ref<json::boolean_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is object");
+        CHECK_THROWS_WITH(value.get_ref<json::number_integer_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is object");
+        CHECK_THROWS_WITH(value.get_ref<json::number_unsigned_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is object");
+        CHECK_THROWS_WITH(value.get_ref<json::number_float_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is object");
     }
 
     SECTION("const reference access to const object_t")
@@ -3095,12 +3131,26 @@
         CHECK(p2 == value.get<test_type>());
 
         // check if mismatching references throw correctly
-        CHECK_THROWS(value.get_ref<json::object_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::object_t&>(), json::type_error);
         CHECK_NOTHROW(value.get_ref<json::array_t&>());
-        CHECK_THROWS(value.get_ref<json::string_t&>());
-        CHECK_THROWS(value.get_ref<json::boolean_t&>());
-        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
-        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::string_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::boolean_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_integer_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_unsigned_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_float_t&>(), json::type_error);
+
+        CHECK_THROWS_WITH(value.get_ref<json::object_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is array");
+        CHECK_THROWS_WITH(value.get_ref<json::string_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is array");
+        CHECK_THROWS_WITH(value.get_ref<json::boolean_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is array");
+        CHECK_THROWS_WITH(value.get_ref<json::number_integer_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is array");
+        CHECK_THROWS_WITH(value.get_ref<json::number_unsigned_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is array");
+        CHECK_THROWS_WITH(value.get_ref<json::number_float_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is array");
     }
 
     SECTION("reference access to string_t")
@@ -3118,12 +3168,26 @@
         CHECK(p2 == value.get<test_type>());
 
         // check if mismatching references throw correctly
-        CHECK_THROWS(value.get_ref<json::object_t&>());
-        CHECK_THROWS(value.get_ref<json::array_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::object_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::array_t&>(), json::type_error);
         CHECK_NOTHROW(value.get_ref<json::string_t&>());
-        CHECK_THROWS(value.get_ref<json::boolean_t&>());
-        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
-        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::boolean_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_integer_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_unsigned_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_float_t&>(), json::type_error);
+
+        CHECK_THROWS_WITH(value.get_ref<json::object_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is string");
+        CHECK_THROWS_WITH(value.get_ref<json::array_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is string");
+        CHECK_THROWS_WITH(value.get_ref<json::boolean_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is string");
+        CHECK_THROWS_WITH(value.get_ref<json::number_integer_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is string");
+        CHECK_THROWS_WITH(value.get_ref<json::number_unsigned_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is string");
+        CHECK_THROWS_WITH(value.get_ref<json::number_float_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is string");
     }
 
     SECTION("reference access to boolean_t")
@@ -3141,12 +3205,26 @@
         CHECK(p2 == value.get<test_type>());
 
         // check if mismatching references throw correctly
-        CHECK_THROWS(value.get_ref<json::object_t&>());
-        CHECK_THROWS(value.get_ref<json::array_t&>());
-        CHECK_THROWS(value.get_ref<json::string_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::object_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::array_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::string_t&>(), json::type_error);
         CHECK_NOTHROW(value.get_ref<json::boolean_t&>());
-        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
-        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::number_integer_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_unsigned_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_float_t&>(), json::type_error);
+
+        CHECK_THROWS_WITH(value.get_ref<json::object_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is boolean");
+        CHECK_THROWS_WITH(value.get_ref<json::array_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is boolean");
+        CHECK_THROWS_WITH(value.get_ref<json::string_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is boolean");
+        CHECK_THROWS_WITH(value.get_ref<json::number_integer_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is boolean");
+        CHECK_THROWS_WITH(value.get_ref<json::number_unsigned_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is boolean");
+        CHECK_THROWS_WITH(value.get_ref<json::number_float_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is boolean");
     }
 
     SECTION("reference access to number_integer_t")
@@ -3164,12 +3242,63 @@
         CHECK(p2 == value.get<test_type>());
 
         // check if mismatching references throw correctly
-        CHECK_THROWS(value.get_ref<json::object_t&>());
-        CHECK_THROWS(value.get_ref<json::array_t&>());
-        CHECK_THROWS(value.get_ref<json::string_t&>());
-        CHECK_THROWS(value.get_ref<json::boolean_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::object_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::array_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::string_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::boolean_t&>(), json::type_error);
         CHECK_NOTHROW(value.get_ref<json::number_integer_t&>());
-        CHECK_THROWS(value.get_ref<json::number_float_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::number_unsigned_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_float_t&>(), json::type_error);
+
+        CHECK_THROWS_WITH(value.get_ref<json::object_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::array_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::string_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::boolean_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::number_unsigned_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::number_float_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+    }
+
+    SECTION("reference access to number_unsigned_t")
+    {
+        using test_type = json::number_unsigned_t;
+        json value = static_cast<json::number_unsigned_t>(23);
+
+        // check if references are returned correctly
+        test_type& p1 = value.get_ref<test_type&>();
+        CHECK(&p1 == value.get_ptr<test_type*>());
+        CHECK(p1 == value.get<test_type>());
+
+        const test_type& p2 = value.get_ref<const test_type&>();
+        CHECK(&p2 == value.get_ptr<const test_type*>());
+        CHECK(p2 == value.get<test_type>());
+
+        // check if mismatching references throw correctly
+        CHECK_THROWS_AS(value.get_ref<json::object_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::array_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::string_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::boolean_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_integer_t&>(), json::type_error);
+        CHECK_NOTHROW(value.get_ref<json::number_unsigned_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::number_float_t&>(), json::type_error);
+
+        CHECK_THROWS_WITH(value.get_ref<json::object_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::array_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::string_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::boolean_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::number_integer_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::number_float_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
     }
 
     SECTION("reference access to number_float_t")
@@ -3187,12 +3316,26 @@
         CHECK(p2 == value.get<test_type>());
 
         // check if mismatching references throw correctly
-        CHECK_THROWS(value.get_ref<json::object_t&>());
-        CHECK_THROWS(value.get_ref<json::array_t&>());
-        CHECK_THROWS(value.get_ref<json::string_t&>());
-        CHECK_THROWS(value.get_ref<json::boolean_t&>());
-        CHECK_THROWS(value.get_ref<json::number_integer_t&>());
+        CHECK_THROWS_AS(value.get_ref<json::object_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::array_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::string_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::boolean_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_integer_t&>(), json::type_error);
+        CHECK_THROWS_AS(value.get_ref<json::number_unsigned_t&>(), json::type_error);
         CHECK_NOTHROW(value.get_ref<json::number_float_t&>());
+
+        CHECK_THROWS_WITH(value.get_ref<json::object_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::array_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::string_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::boolean_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::number_integer_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
+        CHECK_THROWS_WITH(value.get_ref<json::number_unsigned_t&>(),
+                          "[except.310] incompatible ReferenceType for get_ref, actual type is number");
     }
 }
 
@@ -3241,77 +3384,77 @@
                 {
                     json j_nonarray(json::value_t::null);
                     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);
+                    CHECK_THROWS_AS(j_nonarray.at(0), json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error);
 
-                    CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with null");
-                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with null");
+                    CHECK_THROWS_WITH(j_nonarray.at(0), "[except.303] cannot use at() with null");
+                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "[except.303] cannot use at() with null");
                 }
 
                 SECTION("boolean")
                 {
                     json j_nonarray(json::value_t::boolean);
                     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);
+                    CHECK_THROWS_AS(j_nonarray.at(0), json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error);
 
-                    CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with boolean");
-                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with boolean");
+                    CHECK_THROWS_WITH(j_nonarray.at(0), "[except.303] cannot use at() with boolean");
+                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "[except.303] cannot use at() with boolean");
                 }
 
                 SECTION("string")
                 {
                     json j_nonarray(json::value_t::string);
                     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);
+                    CHECK_THROWS_AS(j_nonarray.at(0), json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error);
 
-                    CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with string");
-                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with string");
+                    CHECK_THROWS_WITH(j_nonarray.at(0), "[except.303] cannot use at() with string");
+                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "[except.303] cannot use at() with string");
                 }
 
                 SECTION("object")
                 {
                     json j_nonarray(json::value_t::object);
                     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);
+                    CHECK_THROWS_AS(j_nonarray.at(0), json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error);
 
-                    CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with object");
-                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with object");
+                    CHECK_THROWS_WITH(j_nonarray.at(0), "[except.303] cannot use at() with object");
+                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "[except.303] cannot use at() with object");
                 }
 
                 SECTION("number (integer)")
                 {
                     json j_nonarray(json::value_t::number_integer);
                     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);
+                    CHECK_THROWS_AS(j_nonarray.at(0), json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error);
 
-                    CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number");
-                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonarray.at(0), "[except.303] cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "[except.303] cannot use at() with number");
                 }
 
                 SECTION("number (unsigned)")
                 {
                     json j_nonarray(json::value_t::number_unsigned);
                     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);
+                    CHECK_THROWS_AS(j_nonarray.at(0), json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error);
 
-                    CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number");
-                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonarray.at(0), "[except.303] cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "[except.303] cannot use at() with number");
                 }
 
                 SECTION("number (floating-point)")
                 {
                     json j_nonarray(json::value_t::number_float);
                     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);
+                    CHECK_THROWS_AS(j_nonarray.at(0), json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const.at(0), json::type_error);
 
-                    CHECK_THROWS_WITH(j_nonarray.at(0), "cannot use at() with number");
-                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonarray.at(0), "[except.303] cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonarray_const.at(0), "[except.303] cannot use at() with number");
                 }
             }
         }
@@ -3356,8 +3499,8 @@
                         json j_nonarray(json::value_t::null);
                         const json j_nonarray_const(j_nonarray);
                         CHECK_NOTHROW(j_nonarray[0]);
-                        CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
-                        CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with null");
+                        CHECK_THROWS_AS(j_nonarray_const[0], json::type_error);
+                        CHECK_THROWS_WITH(j_nonarray_const[0], "[except.304] cannot use operator[] with null");
                     }
 
                     SECTION("implicit transformation to properly filled array")
@@ -3372,60 +3515,60 @@
                 {
                     json j_nonarray(json::value_t::boolean);
                     const json j_nonarray_const(j_nonarray);
-                    CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
-                    CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with boolean");
-                    CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with boolean");
+                    CHECK_THROWS_AS(j_nonarray[0], json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const[0], json::type_error);
+                    CHECK_THROWS_WITH(j_nonarray[0], "[except.304] cannot use operator[] with boolean");
+                    CHECK_THROWS_WITH(j_nonarray_const[0], "[except.304] cannot use operator[] with boolean");
                 }
 
                 SECTION("string")
                 {
                     json j_nonarray(json::value_t::string);
                     const json j_nonarray_const(j_nonarray);
-                    CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
-                    CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with string");
-                    CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with string");
+                    CHECK_THROWS_AS(j_nonarray[0], json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const[0], json::type_error);
+                    CHECK_THROWS_WITH(j_nonarray[0], "[except.304] cannot use operator[] with string");
+                    CHECK_THROWS_WITH(j_nonarray_const[0], "[except.304] cannot use operator[] with string");
                 }
 
                 SECTION("object")
                 {
                     json j_nonarray(json::value_t::object);
                     const json j_nonarray_const(j_nonarray);
-                    CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
-                    CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with object");
-                    CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with object");
+                    CHECK_THROWS_AS(j_nonarray[0], json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const[0], json::type_error);
+                    CHECK_THROWS_WITH(j_nonarray[0], "[except.304] cannot use operator[] with object");
+                    CHECK_THROWS_WITH(j_nonarray_const[0], "[except.304] cannot use operator[] with object");
                 }
 
                 SECTION("number (integer)")
                 {
                     json j_nonarray(json::value_t::number_integer);
                     const json j_nonarray_const(j_nonarray);
-                    CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
-                    CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number");
-                    CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number");
+                    CHECK_THROWS_AS(j_nonarray[0], json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const[0], json::type_error);
+                    CHECK_THROWS_WITH(j_nonarray[0], "[except.304] cannot use operator[] with number");
+                    CHECK_THROWS_WITH(j_nonarray_const[0], "[except.304] cannot use operator[] with number");
                 }
 
                 SECTION("number (unsigned)")
                 {
                     json j_nonarray(json::value_t::number_unsigned);
                     const json j_nonarray_const(j_nonarray);
-                    CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
-                    CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number");
-                    CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number");
+                    CHECK_THROWS_AS(j_nonarray[0], json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const[0], json::type_error);
+                    CHECK_THROWS_WITH(j_nonarray[0], "[except.304] cannot use operator[] with number");
+                    CHECK_THROWS_WITH(j_nonarray_const[0], "[except.304] cannot use operator[] with number");
                 }
 
                 SECTION("number (floating-point)")
                 {
                     json j_nonarray(json::value_t::number_float);
                     const json j_nonarray_const(j_nonarray);
-                    CHECK_THROWS_AS(j_nonarray[0], std::domain_error);
-                    CHECK_THROWS_AS(j_nonarray_const[0], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonarray[0], "cannot use operator[] with number");
-                    CHECK_THROWS_WITH(j_nonarray_const[0], "cannot use operator[] with number");
+                    CHECK_THROWS_AS(j_nonarray[0], json::type_error);
+                    CHECK_THROWS_AS(j_nonarray_const[0], json::type_error);
+                    CHECK_THROWS_WITH(j_nonarray[0], "[except.304] cannot use operator[] with number");
+                    CHECK_THROWS_WITH(j_nonarray_const[0], "[except.304] cannot use operator[] with number");
                 }
             }
         }
@@ -3570,34 +3713,36 @@
                     {
                         json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                         json jarray2 = {"foo", "bar"};
-                        CHECK_THROWS_AS(jarray.erase(jarray2.begin()), std::domain_error);
-                        CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), std::domain_error);
-                        CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), std::domain_error);
-                        CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), std::domain_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.begin()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jarray.erase(jarray.begin(), jarray2.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.begin(), jarray2.end()), json::invalid_iterator);
 
-                        CHECK_THROWS_WITH(jarray.erase(jarray2.begin()), "iterator does not fit current value");
+                        CHECK_THROWS_WITH(jarray.erase(jarray2.begin()),
+                                          "[except.103] iterator does not fit current value");
                         CHECK_THROWS_WITH(jarray.erase(jarray.begin(), jarray2.end()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray.end()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jarray.erase(jarray2.begin(), jarray2.end()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                     }
                     {
                         json jarray = {1, 1u, true, nullptr, "string", 42.23, json::object(), {1, 2, 3}};
                         json jarray2 = {"foo", "bar"};
-                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), std::domain_error);
-                        CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), std::domain_error);
-                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), std::domain_error);
-                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), std::domain_error);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jarray.erase(jarray.cbegin(), jarray2.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jarray.erase(jarray2.cbegin(), jarray2.cend()), json::invalid_iterator);
 
-                        CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()), "iterator does not fit current value");
+                        CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin()),
+                                          "[except.103] iterator does not fit current value");
                         CHECK_THROWS_WITH(jarray.erase(jarray.cbegin(), jarray2.cend()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray.cend()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jarray.erase(jarray2.cbegin(), jarray2.cend()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                     }
                 }
             }
@@ -3607,50 +3752,50 @@
                 SECTION("null")
                 {
                     json j_nonobject(json::value_t::null);
-                    CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with null");
+                    CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase(0), "[except.306] cannot use erase() with null");
                 }
 
                 SECTION("boolean")
                 {
                     json j_nonobject(json::value_t::boolean);
-                    CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with boolean");
+                    CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase(0), "[except.306] cannot use erase() with boolean");
                 }
 
                 SECTION("string")
                 {
                     json j_nonobject(json::value_t::string);
-                    CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with string");
+                    CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase(0), "[except.306] cannot use erase() with string");
                 }
 
                 SECTION("object")
                 {
                     json j_nonobject(json::value_t::object);
-                    CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with object");
+                    CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase(0), "[except.306] cannot use erase() with object");
                 }
 
                 SECTION("number (integer)")
                 {
                     json j_nonobject(json::value_t::number_integer);
-                    CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number");
+                    CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase(0), "[except.306] cannot use erase() with number");
                 }
 
                 SECTION("number (unsigned)")
                 {
                     json j_nonobject(json::value_t::number_unsigned);
-                    CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number");
+                    CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase(0), "[except.306] cannot use erase() with number");
                 }
 
                 SECTION("number (floating-point)")
                 {
                     json j_nonobject(json::value_t::number_float);
-                    CHECK_THROWS_AS(j_nonobject.erase(0), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase(0), "cannot use erase() with number");
+                    CHECK_THROWS_AS(j_nonobject.erase(0), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase(0), "[except.306] cannot use erase() with number");
                 }
             }
         }
@@ -3698,70 +3843,70 @@
                 {
                     json j_nonobject(json::value_t::null);
                     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);
-                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with null");
-                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with null");
+                    CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error);
+                    CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "[except.303] cannot use at() with null");
+                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[except.303] cannot use at() with null");
                 }
 
                 SECTION("boolean")
                 {
                     json j_nonobject(json::value_t::boolean);
                     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);
-                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with boolean");
-                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with boolean");
+                    CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error);
+                    CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "[except.303] cannot use at() with boolean");
+                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[except.303] cannot use at() with boolean");
                 }
 
                 SECTION("string")
                 {
                     json j_nonobject(json::value_t::string);
                     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);
-                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with string");
-                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with string");
+                    CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error);
+                    CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "[except.303] cannot use at() with string");
+                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[except.303] cannot use at() with string");
                 }
 
                 SECTION("array")
                 {
                     json j_nonobject(json::value_t::array);
                     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);
-                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with array");
-                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with array");
+                    CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error);
+                    CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "[except.303] cannot use at() with array");
+                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[except.303] cannot use at() with array");
                 }
 
                 SECTION("number (integer)")
                 {
                     json j_nonobject(json::value_t::number_integer);
                     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);
-                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number");
-                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number");
+                    CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error);
+                    CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "[except.303] cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[except.303] cannot use at() with number");
                 }
 
                 SECTION("number (unsigned)")
                 {
                     json j_nonobject(json::value_t::number_unsigned);
                     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);
-                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number");
-                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number");
+                    CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error);
+                    CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "[except.303] cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[except.303] cannot use at() with number");
                 }
 
                 SECTION("number (floating-point)")
                 {
                     json j_nonobject(json::value_t::number_float);
                     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);
-                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "cannot use at() with number");
-                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "cannot use at() with number");
+                    CHECK_THROWS_AS(j_nonobject.at("foo"), json::type_error);
+                    CHECK_THROWS_AS(j_nonobject_const.at("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.at("foo"), "[except.303] cannot use at() with number");
+                    CHECK_THROWS_WITH(j_nonobject_const.at("foo"), "[except.303] cannot use at() with number");
                 }
             }
         }
@@ -3823,70 +3968,71 @@
                     {
                         json j_nonobject(json::value_t::null);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with null");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with null");
+                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[except.305] cannot use value() with null");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[except.305] cannot use value() with null");
                     }
 
                     SECTION("boolean")
                     {
                         json j_nonobject(json::value_t::boolean);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with boolean");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with boolean");
+                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[except.305] cannot use value() with boolean");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1),
+                                          "[except.305] cannot use value() with boolean");
                     }
 
                     SECTION("string")
                     {
                         json j_nonobject(json::value_t::string);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with string");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with string");
+                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[except.305] cannot use value() with string");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[except.305] cannot use value() with string");
                     }
 
                     SECTION("array")
                     {
                         json j_nonobject(json::value_t::array);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with array");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with array");
+                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[except.305] cannot use value() with array");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[except.305] cannot use value() with array");
                     }
 
                     SECTION("number (integer)")
                     {
                         json j_nonobject(json::value_t::number_integer);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[except.305] cannot use value() with number");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[except.305] cannot use value() with number");
                     }
 
                     SECTION("number (unsigned)")
                     {
                         json j_nonobject(json::value_t::number_unsigned);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[except.305] cannot use value() with number");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[except.305] cannot use value() with number");
                     }
 
                     SECTION("number (floating-point)")
                     {
                         json j_nonobject(json::value_t::number_float);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "cannot use value() with number");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "cannot use value() with number");
+                        CHECK_THROWS_AS(j_nonobject.value("foo", 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("foo", 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("foo", 1), "[except.305] cannot use value() with number");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("foo", 1), "[except.305] cannot use value() with number");
                     }
                 }
             }
@@ -3946,75 +4092,84 @@
                     {
                         json j_nonobject(json::value_t::null);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with null");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with null");
+                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with null");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with null");
                     }
 
                     SECTION("boolean")
                     {
                         json j_nonobject(json::value_t::boolean);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with boolean");
+                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with boolean");
                         CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
-                                          "cannot use value() with boolean");
+                                          "[except.305] cannot use value() with boolean");
                     }
 
                     SECTION("string")
                     {
                         json j_nonobject(json::value_t::string);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with string");
+                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with string");
                         CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
-                                          "cannot use value() with string");
+                                          "[except.305] cannot use value() with string");
                     }
 
                     SECTION("array")
                     {
                         json j_nonobject(json::value_t::array);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with array");
-                        CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1), "cannot use value() with array");
+                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with array");
+                        CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with array");
                     }
 
                     SECTION("number (integer)")
                     {
                         json j_nonobject(json::value_t::number_integer);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
+                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with number");
                         CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
-                                          "cannot use value() with number");
+                                          "[except.305] cannot use value() with number");
                     }
 
                     SECTION("number (unsigned)")
                     {
                         json j_nonobject(json::value_t::number_unsigned);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
+                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with number");
                         CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
-                                          "cannot use value() with number");
+                                          "[except.305] cannot use value() with number");
                     }
 
                     SECTION("number (floating-point)")
                     {
                         json j_nonobject(json::value_t::number_float);
                         const json j_nonobject_const(j_nonobject);
-                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), std::domain_error);
-                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1), "cannot use value() with number");
+                        CHECK_THROWS_AS(j_nonobject.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_AS(j_nonobject_const.value("/foo"_json_pointer, 1), json::type_error);
+                        CHECK_THROWS_WITH(j_nonobject.value("/foo"_json_pointer, 1),
+                                          "[except.305] cannot use value() with number");
                         CHECK_THROWS_WITH(j_nonobject_const.value("/foo"_json_pointer, 1),
-                                          "cannot use value() with number");
+                                          "[except.305] cannot use value() with number");
                     }
                 }
             }
@@ -4089,106 +4244,120 @@
                     const json j_const_nonobject(j_nonobject);
                     CHECK_NOTHROW(j_nonobject["foo"]);
                     CHECK_NOTHROW(j_nonobject2[json::object_t::key_type("foo")]);
-                    CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with null");
+                    CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_WITH(j_const_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with null");
                     CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with null");
+                                      "[except.304] cannot use operator[] with null");
                 }
 
                 SECTION("boolean")
                 {
                     json j_nonobject(json::value_t::boolean);
                     const json j_const_nonobject(j_nonobject);
-                    CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with boolean");
+                    CHECK_THROWS_AS(j_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with boolean");
                     CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with boolean");
-                    CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with boolean");
+                                      "[except.304] cannot use operator[] with boolean");
+                    CHECK_THROWS_WITH(j_const_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with boolean");
                     CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with boolean");
+                                      "[except.304] cannot use operator[] with boolean");
                 }
 
                 SECTION("string")
                 {
                     json j_nonobject(json::value_t::string);
                     const json j_const_nonobject(j_nonobject);
-                    CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with string");
+                    CHECK_THROWS_AS(j_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with string");
                     CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with string");
-                    CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with string");
+                                      "[except.304] cannot use operator[] with string");
+                    CHECK_THROWS_WITH(j_const_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with string");
                     CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with string");
+                                      "[except.304] cannot use operator[] with string");
                 }
 
                 SECTION("array")
                 {
                     json j_nonobject(json::value_t::array);
                     const json j_const_nonobject(j_nonobject);
-                    CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with array");
-                    CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")], "cannot use operator[] with array");
-                    CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with array");
+                    CHECK_THROWS_AS(j_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with array");
+                    CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
+                                      "[except.304] cannot use operator[] with array");
+                    CHECK_THROWS_WITH(j_const_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with array");
                     CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with array");
+                                      "[except.304] cannot use operator[] with array");
                 }
 
                 SECTION("number (integer)")
                 {
                     json j_nonobject(json::value_t::number_integer);
                     const json j_const_nonobject(j_nonobject);
-                    CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number");
+                    CHECK_THROWS_AS(j_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with number");
                     CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with number");
-                    CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number");
+                                      "[except.304] cannot use operator[] with number");
+                    CHECK_THROWS_WITH(j_const_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with number");
                     CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with number");
+                                      "[except.304] cannot use operator[] with number");
                 }
 
                 SECTION("number (unsigned)")
                 {
                     json j_nonobject(json::value_t::number_unsigned);
                     const json j_const_nonobject(j_nonobject);
-                    CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number");
+                    CHECK_THROWS_AS(j_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with number");
                     CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with number");
-                    CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number");
+                                      "[except.304] cannot use operator[] with number");
+                    CHECK_THROWS_WITH(j_const_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with number");
                     CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with number");
+                                      "[except.304] cannot use operator[] with number");
                 }
 
                 SECTION("number (floating-point)")
                 {
                     json j_nonobject(json::value_t::number_float);
                     const json j_const_nonobject(j_nonobject);
-                    CHECK_THROWS_AS(j_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject["foo"], std::domain_error);
-                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject["foo"], "cannot use operator[] with number");
+                    CHECK_THROWS_AS(j_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject["foo"], json::type_error);
+                    CHECK_THROWS_AS(j_const_nonobject[json::object_t::key_type("foo")], json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with number");
                     CHECK_THROWS_WITH(j_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with number");
-                    CHECK_THROWS_WITH(j_const_nonobject["foo"], "cannot use operator[] with number");
+                                      "[except.304] cannot use operator[] with number");
+                    CHECK_THROWS_WITH(j_const_nonobject["foo"],
+                                      "[except.304] cannot use operator[] with number");
                     CHECK_THROWS_WITH(j_const_nonobject[json::object_t::key_type("foo")],
-                                      "cannot use operator[] with number");
+                                      "[except.304] cannot use operator[] with number");
                 }
             }
         }
@@ -4327,32 +4496,34 @@
                     {
                         json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
                         json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-                        CHECK_THROWS_AS(jobject.erase(jobject2.begin()), std::domain_error);
-                        CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), std::domain_error);
-                        CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), std::domain_error);
-                        CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), std::domain_error);
-                        CHECK_THROWS_WITH(jobject.erase(jobject2.begin()), "iterator does not fit current value");
+                        CHECK_THROWS_AS(jobject.erase(jobject2.begin()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jobject.erase(jobject.begin(), jobject2.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject.end()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.begin(), jobject2.end()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(jobject.erase(jobject2.begin()),
+                                          "[except.103] iterator does not fit current value");
                         CHECK_THROWS_WITH(jobject.erase(jobject.begin(), jobject2.end()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject.end()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jobject.erase(jobject2.begin(), jobject2.end()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                     }
                     {
                         json jobject = {{"a", "a"}, {"b", 1}, {"c", 17u}, {"d", false}, {"e", true}};
                         json jobject2 = {{"a", "a"}, {"b", 1}, {"c", 17u}};
-                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), std::domain_error);
-                        CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), std::domain_error);
-                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), std::domain_error);
-                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), std::domain_error);
-                        CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()), "iterator does not fit current value");
+                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jobject.erase(jobject.cbegin(), jobject2.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject.cend()), json::invalid_iterator);
+                        CHECK_THROWS_AS(jobject.erase(jobject2.cbegin(), jobject2.cend()), json::invalid_iterator);
+                        CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin()),
+                                          "[except.103] iterator does not fit current value");
                         CHECK_THROWS_WITH(jobject.erase(jobject.cbegin(), jobject2.cend()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject.cend()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                         CHECK_THROWS_WITH(jobject.erase(jobject2.cbegin(), jobject2.cend()),
-                                          "iterators do not fit current value");
+                                          "[except.102] iterators do not fit current value");
                     }
                 }
             }
@@ -4362,43 +4533,43 @@
                 SECTION("null")
                 {
                     json j_nonobject(json::value_t::null);
-                    CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with null");
+                    CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[except.306] cannot use erase() with null");
                 }
 
                 SECTION("boolean")
                 {
                     json j_nonobject(json::value_t::boolean);
-                    CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with boolean");
+                    CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[except.306] cannot use erase() with boolean");
                 }
 
                 SECTION("string")
                 {
                     json j_nonobject(json::value_t::string);
-                    CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with string");
+                    CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[except.306] cannot use erase() with string");
                 }
 
                 SECTION("array")
                 {
                     json j_nonobject(json::value_t::array);
-                    CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with array");
+                    CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[except.306] cannot use erase() with array");
                 }
 
                 SECTION("number (integer)")
                 {
                     json j_nonobject(json::value_t::number_integer);
-                    CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number");
+                    CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[except.306] cannot use erase() with number");
                 }
 
                 SECTION("number (floating-point)")
                 {
                     json j_nonobject(json::value_t::number_float);
-                    CHECK_THROWS_AS(j_nonobject.erase("foo"), std::domain_error);
-                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "cannot use erase() with number");
+                    CHECK_THROWS_AS(j_nonobject.erase("foo"), json::type_error);
+                    CHECK_THROWS_WITH(j_nonobject.erase("foo"), "[except.306] cannot use erase() with number");
                 }
             }
         }
@@ -4588,17 +4759,17 @@
             {
                 {
                     json j;
-                    CHECK_THROWS_AS(j.front(), std::out_of_range);
-                    CHECK_THROWS_AS(j.back(), std::out_of_range);
-                    CHECK_THROWS_WITH(j.front(), "cannot get value");
-                    CHECK_THROWS_WITH(j.back(), "cannot get value");
+                    CHECK_THROWS_AS(j.front(), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.back(), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.front(), "[except.114] cannot get value");
+                    CHECK_THROWS_WITH(j.back(), "[except.114] cannot get value");
                 }
                 {
                     const json j{};
-                    CHECK_THROWS_AS(j.front(), std::out_of_range);
-                    CHECK_THROWS_AS(j.back(), std::out_of_range);
-                    CHECK_THROWS_WITH(j.front(), "cannot get value");
-                    CHECK_THROWS_WITH(j.back(), "cannot get value");
+                    CHECK_THROWS_AS(j.front(), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.back(), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.front(), "[except.114] cannot get value");
+                    CHECK_THROWS_WITH(j.back(), "[except.114] cannot get value");
                 }
             }
 
@@ -4679,13 +4850,13 @@
             {
                 {
                     json j;
-                    CHECK_THROWS_AS(j.erase(j.begin()), std::domain_error);
-                    CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null");
+                    CHECK_THROWS_AS(j.erase(j.begin()), json::type_error);
+                    CHECK_THROWS_WITH(j.erase(j.begin()), "[except.306] cannot use erase() with null");
                 }
                 {
                     json j;
-                    CHECK_THROWS_AS(j.erase(j.cbegin()), std::domain_error);
-                    CHECK_THROWS_WITH(j.erase(j.begin()), "cannot use erase() with null");
+                    CHECK_THROWS_AS(j.erase(j.cbegin()), json::type_error);
+                    CHECK_THROWS_WITH(j.erase(j.begin()), "[except.306] cannot use erase() with null");
                 }
             }
 
@@ -4776,13 +4947,13 @@
             {
                 {
                     json j = "foo";
-                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end()), "[except.105] iterator out of range");
                 }
                 {
                     json j = "bar";
-                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend()), "[except.105] iterator out of range");
                 }
             }
 
@@ -4790,13 +4961,13 @@
             {
                 {
                     json j = false;
-                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end()), "[except.105] iterator out of range");
                 }
                 {
                     json j = true;
-                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend()), "[except.105] iterator out of range");
                 }
             }
 
@@ -4804,13 +4975,13 @@
             {
                 {
                     json j = 17;
-                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end()), "[except.105] iterator out of range");
                 }
                 {
                     json j = 17;
-                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend()), "[except.105] iterator out of range");
                 }
             }
 
@@ -4818,13 +4989,13 @@
             {
                 {
                     json j = 17u;
-                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end()), "[except.105] iterator out of range");
                 }
                 {
                     json j = 17u;
-                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend()), "[except.105] iterator out of range");
                 }
             }
 
@@ -4832,13 +5003,13 @@
             {
                 {
                     json j = 23.42;
-                    CHECK_THROWS_AS(j.erase(j.end()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.end()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end()), "[except.105] iterator out of range");
                 }
                 {
                     json j = 23.42;
-                    CHECK_THROWS_AS(j.erase(j.cend()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend()), "iterator out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend()), "[except.105] iterator out of range");
                 }
             }
         }
@@ -4849,13 +5020,13 @@
             {
                 {
                     json j;
-                    CHECK_THROWS_AS(j.erase(j.begin(), j.end()), std::domain_error);
-                    CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "cannot use erase() with null");
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.end()), json::type_error);
+                    CHECK_THROWS_WITH(j.erase(j.begin(), j.end()), "[except.306] cannot use erase() with null");
                 }
                 {
                     json j;
-                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), std::domain_error);
-                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "cannot use erase() with null");
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cend()), json::type_error);
+                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cend()), "[except.306] cannot use erase() with null");
                 }
             }
 
@@ -4946,17 +5117,17 @@
             {
                 {
                     json j = "foo";
-                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[except.104] iterators out of range");
                 }
                 {
                     json j = "bar";
-                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                 }
             }
 
@@ -4964,17 +5135,17 @@
             {
                 {
                     json j = false;
-                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[except.104] iterators out of range");
                 }
                 {
                     json j = true;
-                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                 }
             }
 
@@ -4982,17 +5153,17 @@
             {
                 {
                     json j = 17;
-                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[except.104] iterators out of range");
                 }
                 {
                     json j = 17;
-                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                 }
             }
 
@@ -5000,17 +5171,17 @@
             {
                 {
                     json j = 17u;
-                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[except.104] iterators out of range");
                 }
                 {
                     json j = 17u;
-                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                 }
             }
 
@@ -5018,17 +5189,17 @@
             {
                 {
                     json j = 23.42;
-                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.end(), j.end()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.begin(), j.begin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.end(), j.end()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.begin(), j.begin()), "[except.104] iterators out of range");
                 }
                 {
                     json j = 23.42;
-                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), std::out_of_range);
-                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), std::out_of_range);
-                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "iterators out of range");
-                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "iterators out of range");
+                    CHECK_THROWS_AS(j.erase(j.cend(), j.cend()), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.erase(j.cbegin(), j.cbegin()), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.erase(j.cend(), j.cend()), "[except.104] iterators out of range");
+                    CHECK_THROWS_WITH(j.erase(j.cbegin(), j.cbegin()), "[except.104] iterators out of range");
                 }
             }
         }
@@ -5232,23 +5403,23 @@
             {
                 auto it = j.begin();
                 auto cit = j_const.cbegin();
-                CHECK_THROWS_AS(it.key(), std::domain_error);
-                CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(it.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(it.value() == json(true));
-                CHECK_THROWS_AS(cit.key(), std::domain_error);
-                CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(cit.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(cit.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(cit.value() == json(true));
 
                 auto rit = j.rend();
                 auto crit = j.crend();
-                CHECK_THROWS_AS(rit.key(), std::domain_error);
-                CHECK_THROWS_AS(rit.value(), std::out_of_range);
-                CHECK_THROWS_AS(crit.key(), std::domain_error);
-                CHECK_THROWS_AS(crit.value(), std::out_of_range);
-                CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(rit.value(), "cannot get value");
-                CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(crit.value(), "cannot get value");
+                CHECK_THROWS_AS(rit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(rit.value(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.value(), json::invalid_iterator);
+                CHECK_THROWS_WITH(rit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(rit.value(), "[except.114] cannot get value");
+                CHECK_THROWS_WITH(crit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(crit.value(), "[except.114] cannot get value");
             }
         }
 
@@ -5436,23 +5607,23 @@
             {
                 auto it = j.begin();
                 auto cit = j_const.cbegin();
-                CHECK_THROWS_AS(it.key(), std::domain_error);
-                CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(it.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(it.value() == json("hello world"));
-                CHECK_THROWS_AS(cit.key(), std::domain_error);
-                CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(cit.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(cit.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(cit.value() == json("hello world"));
 
                 auto rit = j.rend();
                 auto crit = j.crend();
-                CHECK_THROWS_AS(rit.key(), std::domain_error);
-                CHECK_THROWS_AS(rit.value(), std::out_of_range);
-                CHECK_THROWS_AS(crit.key(), std::domain_error);
-                CHECK_THROWS_AS(crit.value(), std::out_of_range);
-                CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(rit.value(), "cannot get value");
-                CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(crit.value(), "cannot get value");
+                CHECK_THROWS_AS(rit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(rit.value(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.value(), json::invalid_iterator);
+                CHECK_THROWS_WITH(rit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(rit.value(), "[except.114] cannot get value");
+                CHECK_THROWS_WITH(crit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(crit.value(), "[except.114] cannot get value");
             }
         }
 
@@ -5633,11 +5804,11 @@
             {
                 auto it = j.begin();
                 auto cit = j_const.cbegin();
-                CHECK_THROWS_AS(it.key(), std::domain_error);
-                CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(it.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(it.value() == json(1));
-                CHECK_THROWS_AS(cit.key(), std::domain_error);
-                CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(cit.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(cit.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(cit.value() == json(1));
             }
         }
@@ -6010,23 +6181,23 @@
             {
                 auto it = j.begin();
                 auto cit = j_const.cbegin();
-                CHECK_THROWS_AS(it.key(), std::domain_error);
-                CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(it.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(it.value() == json(23));
-                CHECK_THROWS_AS(cit.key(), std::domain_error);
-                CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(cit.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(cit.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(cit.value() == json(23));
 
                 auto rit = j.rend();
                 auto crit = j.crend();
-                CHECK_THROWS_AS(rit.key(), std::domain_error);
-                CHECK_THROWS_AS(rit.value(), std::out_of_range);
-                CHECK_THROWS_AS(crit.key(), std::domain_error);
-                CHECK_THROWS_AS(crit.value(), std::out_of_range);
-                CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(rit.value(), "cannot get value");
-                CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(crit.value(), "cannot get value");
+                CHECK_THROWS_AS(rit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(rit.value(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.value(), json::invalid_iterator);
+                CHECK_THROWS_WITH(rit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(rit.value(), "[except.114] cannot get value");
+                CHECK_THROWS_WITH(crit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(crit.value(), "[except.114] cannot get value");
             }
         }
 
@@ -6214,23 +6385,23 @@
             {
                 auto it = j.begin();
                 auto cit = j_const.cbegin();
-                CHECK_THROWS_AS(it.key(), std::domain_error);
-                CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(it.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(it.value() == json(23));
-                CHECK_THROWS_AS(cit.key(), std::domain_error);
-                CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(cit.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(cit.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(cit.value() == json(23));
 
                 auto rit = j.rend();
                 auto crit = j.crend();
-                CHECK_THROWS_AS(rit.key(), std::domain_error);
-                CHECK_THROWS_AS(rit.value(), std::out_of_range);
-                CHECK_THROWS_AS(crit.key(), std::domain_error);
-                CHECK_THROWS_AS(crit.value(), std::out_of_range);
-                CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(rit.value(), "cannot get value");
-                CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(crit.value(), "cannot get value");
+                CHECK_THROWS_AS(rit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(rit.value(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.value(), json::invalid_iterator);
+                CHECK_THROWS_WITH(rit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(rit.value(), "[except.114] cannot get value");
+                CHECK_THROWS_WITH(crit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(crit.value(), "[except.114] cannot get value");
             }
         }
 
@@ -6418,23 +6589,23 @@
             {
                 auto it = j.begin();
                 auto cit = j_const.cbegin();
-                CHECK_THROWS_AS(it.key(), std::domain_error);
-                CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(it.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(it.value() == json(23.42));
-                CHECK_THROWS_AS(cit.key(), std::domain_error);
-                CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
+                CHECK_THROWS_AS(cit.key(), json::invalid_iterator);
+                CHECK_THROWS_WITH(cit.key(), "[except.107] cannot use key() for non-object iterators");
                 CHECK(cit.value() == json(23.42));
 
                 auto rit = j.rend();
                 auto crit = j.crend();
-                CHECK_THROWS_AS(rit.key(), std::domain_error);
-                CHECK_THROWS_AS(rit.value(), std::out_of_range);
-                CHECK_THROWS_AS(crit.key(), std::domain_error);
-                CHECK_THROWS_AS(crit.value(), std::out_of_range);
-                CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(rit.value(), "cannot get value");
-                CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(crit.value(), "cannot get value");
+                CHECK_THROWS_AS(rit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(rit.value(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.value(), json::invalid_iterator);
+                CHECK_THROWS_WITH(rit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(rit.value(), "[except.114] cannot get value");
+                CHECK_THROWS_WITH(crit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(crit.value(), "[except.114] cannot get value");
             }
         }
 
@@ -6492,25 +6663,25 @@
             {
                 auto it = j.begin();
                 auto cit = j_const.cbegin();
-                CHECK_THROWS_AS(it.key(), std::domain_error);
-                CHECK_THROWS_AS(it.value(), std::out_of_range);
-                CHECK_THROWS_AS(cit.key(), std::domain_error);
-                CHECK_THROWS_AS(cit.value(), std::out_of_range);
-                CHECK_THROWS_WITH(it.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(it.value(), "cannot get value");
-                CHECK_THROWS_WITH(cit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(cit.value(), "cannot get value");
+                CHECK_THROWS_AS(it.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(it.value(), json::invalid_iterator);
+                CHECK_THROWS_AS(cit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(cit.value(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(it.value(), "[except.114] cannot get value");
+                CHECK_THROWS_WITH(cit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(cit.value(), "[except.114] cannot get value");
 
                 auto rit = j.rend();
                 auto crit = j.crend();
-                CHECK_THROWS_AS(rit.key(), std::domain_error);
-                CHECK_THROWS_AS(rit.value(), std::out_of_range);
-                CHECK_THROWS_AS(crit.key(), std::domain_error);
-                CHECK_THROWS_AS(crit.value(), std::out_of_range);
-                CHECK_THROWS_WITH(rit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(rit.value(), "cannot get value");
-                CHECK_THROWS_WITH(crit.key(), "cannot use key() for non-object iterators");
-                CHECK_THROWS_WITH(crit.value(), "cannot get value");
+                CHECK_THROWS_AS(rit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(rit.value(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.key(), json::invalid_iterator);
+                CHECK_THROWS_AS(crit.value(), json::invalid_iterator);
+                CHECK_THROWS_WITH(rit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(rit.value(), "[except.114] cannot get value");
+                CHECK_THROWS_WITH(crit.key(), "[except.107] cannot use key() for non-object iterators");
+                CHECK_THROWS_WITH(crit.value(), "[except.114] cannot get value");
             }
         }
     }
@@ -6563,22 +6734,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 < it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 < it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 < it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 < it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c < it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c < it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c < it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c < it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 < it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 < it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 < it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 < it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c < it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 < it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 < it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 < it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 < it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c < it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c < it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c < it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c < it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -6597,22 +6768,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 <= it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 <= it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 <= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 <= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 <= it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 <= it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 <= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 <= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c <= it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 <= it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 <= it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 <= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 <= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c <= it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c <= it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c <= it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c <= it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -6632,22 +6803,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 > it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 > it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 > it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 > it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c > it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c > it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c > it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c > it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 > it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 > it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 > it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 > it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c > it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 > it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 > it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 > it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 > it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c > it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c > it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c > it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c > it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -6667,22 +6838,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 >= it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 >= it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 >= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 >= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 >= it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 >= it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 >= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 >= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c >= it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 >= it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 >= it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 >= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 >= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c >= it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c >= it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c >= it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c >= it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -6706,15 +6877,19 @@
             {
                 if (j != k)
                 {
-                    CHECK_THROWS_AS(j.begin() == k.begin(), std::domain_error);
-                    CHECK_THROWS_AS(j.cbegin() == k.cbegin(), std::domain_error);
-                    CHECK_THROWS_WITH(j.begin() == k.begin(), "cannot compare iterators of different containers");
-                    CHECK_THROWS_WITH(j.cbegin() == k.cbegin(), "cannot compare iterators of different containers");
+                    CHECK_THROWS_AS(j.begin() == k.begin(), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.cbegin() == k.cbegin(), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.begin() == k.begin(),
+                                      "[except.112] cannot compare iterators of different containers");
+                    CHECK_THROWS_WITH(j.cbegin() == k.cbegin(),
+                                      "[except.112] cannot compare iterators of different containers");
 
-                    CHECK_THROWS_AS(j.begin() < k.begin(), std::domain_error);
-                    CHECK_THROWS_AS(j.cbegin() < k.cbegin(), std::domain_error);
-                    CHECK_THROWS_WITH(j.begin() < k.begin(), "cannot compare iterators of different containers");
-                    CHECK_THROWS_WITH(j.cbegin() < k.cbegin(), "cannot compare iterators of different containers");
+                    CHECK_THROWS_AS(j.begin() < k.begin(), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.cbegin() < k.cbegin(), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.begin() < k.begin(),
+                                      "[except.112] cannot compare iterators of different containers");
+                    CHECK_THROWS_WITH(j.cbegin() < k.cbegin(),
+                                      "[except.112] cannot compare iterators of different containers");
                 }
             }
         }
@@ -6733,53 +6908,53 @@
             {
                 {
                     auto it = j_object.begin();
-                    CHECK_THROWS_AS(it += 1, std::domain_error);
-                    CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it += 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it += 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.cbegin();
-                    CHECK_THROWS_AS(it += 1, std::domain_error);
-                    CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it += 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it += 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.begin();
-                    CHECK_THROWS_AS(it + 1, std::domain_error);
-                    CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it + 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it + 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.cbegin();
-                    CHECK_THROWS_AS(it + 1, std::domain_error);
-                    CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it + 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it + 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.begin();
-                    CHECK_THROWS_AS(it -= 1, std::domain_error);
-                    CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it -= 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it -= 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.cbegin();
-                    CHECK_THROWS_AS(it -= 1, std::domain_error);
-                    CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it -= 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it -= 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.begin();
-                    CHECK_THROWS_AS(it - 1, std::domain_error);
-                    CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.cbegin();
-                    CHECK_THROWS_AS(it - 1, std::domain_error);
-                    CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.begin();
-                    CHECK_THROWS_AS(it - it, std::domain_error);
-                    CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - it, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - it, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.cbegin();
-                    CHECK_THROWS_AS(it - it, std::domain_error);
-                    CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - it, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - it, "[except.109] cannot use offsets with object iterators");
                 }
             }
 
@@ -6862,17 +7037,17 @@
             {
                 {
                     auto it = j_object.begin();
-                    CHECK_THROWS_AS(it[0], std::domain_error);
-                    CHECK_THROWS_AS(it[1], std::domain_error);
-                    CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators");
-                    CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.108] cannot use operator[] for object iterators");
+                    CHECK_THROWS_WITH(it[1], "[except.108] cannot use operator[] for object iterators");
                 }
                 {
                     auto it = j_object.cbegin();
-                    CHECK_THROWS_AS(it[0], std::domain_error);
-                    CHECK_THROWS_AS(it[1], std::domain_error);
-                    CHECK_THROWS_WITH(it[0], "cannot use operator[] for object iterators");
-                    CHECK_THROWS_WITH(it[1], "cannot use operator[] for object iterators");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.108] cannot use operator[] for object iterators");
+                    CHECK_THROWS_WITH(it[1], "[except.108] cannot use operator[] for object iterators");
                 }
             }
 
@@ -6902,17 +7077,17 @@
             {
                 {
                     auto it = j_null.begin();
-                    CHECK_THROWS_AS(it[0], std::out_of_range);
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[0], "cannot get value");
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.114] cannot get value");
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
                 {
                     auto it = j_null.cbegin();
-                    CHECK_THROWS_AS(it[0], std::out_of_range);
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[0], "cannot get value");
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.114] cannot get value");
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
             }
 
@@ -6921,14 +7096,14 @@
                 {
                     auto it = j_value.begin();
                     CHECK(it[0] == json(42));
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
                 {
                     auto it = j_value.cbegin();
                     CHECK(it[0] == json(42));
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
             }
         }
@@ -6982,22 +7157,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 < it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 < it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 < it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 < it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c < it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c < it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c < it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c < it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 < it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 < it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 < it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 < it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c < it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c < it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c < it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c < it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 < it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 < it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 < it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 < it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c < it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c < it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c < it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c < it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 < it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 < it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 < it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 < it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c < it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c < it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c < it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c < it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -7016,22 +7191,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 <= it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 <= it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 <= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 <= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c <= it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c <= it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c <= it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c <= it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 <= it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 <= it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 <= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 <= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c <= it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c <= it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c <= it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c <= it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 <= it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 <= it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 <= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 <= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c <= it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c <= it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c <= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c <= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 <= it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 <= it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 <= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 <= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c <= it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c <= it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c <= it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c <= it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -7051,22 +7226,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 > it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 > it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 > it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 > it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c > it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c > it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c > it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c > it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 > it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 > it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 > it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 > it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c > it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c > it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c > it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c > it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 > it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 > it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 > it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 > it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c > it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c > it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c > it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c > it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 > it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 > it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 > it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 > it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c > it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c > it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c > it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c > it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -7086,22 +7261,22 @@
             {
                 if (j.type() == json::value_t::object)
                 {
-                    CHECK_THROWS_AS(it1 >= it1, std::domain_error);
-                    CHECK_THROWS_AS(it1 >= it2, std::domain_error);
-                    CHECK_THROWS_AS(it2 >= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1 >= it3, std::domain_error);
-                    CHECK_THROWS_AS(it1_c >= it1_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c >= it2_c, std::domain_error);
-                    CHECK_THROWS_AS(it2_c >= it3_c, std::domain_error);
-                    CHECK_THROWS_AS(it1_c >= it3_c, std::domain_error);
-                    CHECK_THROWS_WITH(it1 >= it1, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 >= it2, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2 >= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1 >= it3, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c >= it1_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c >= it2_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it2_c >= it3_c, "cannot compare order of object iterators");
-                    CHECK_THROWS_WITH(it1_c >= it3_c, "cannot compare order of object iterators");
+                    CHECK_THROWS_AS(it1 >= it1, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 >= it2, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2 >= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1 >= it3, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c >= it1_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c >= it2_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it2_c >= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_AS(it1_c >= it3_c, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it1 >= it1, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 >= it2, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2 >= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1 >= it3, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c >= it1_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c >= it2_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it2_c >= it3_c, "[except.113] cannot compare order of object iterators");
+                    CHECK_THROWS_WITH(it1_c >= it3_c, "[except.113] cannot compare order of object iterators");
                 }
                 else
                 {
@@ -7125,15 +7300,19 @@
             {
                 if (j != k)
                 {
-                    CHECK_THROWS_AS(j.rbegin() == k.rbegin(), std::domain_error);
-                    CHECK_THROWS_AS(j.crbegin() == k.crbegin(), std::domain_error);
-                    CHECK_THROWS_WITH(j.rbegin() == k.rbegin(), "cannot compare iterators of different containers");
-                    CHECK_THROWS_WITH(j.crbegin() == k.crbegin(), "cannot compare iterators of different containers");
+                    CHECK_THROWS_AS(j.rbegin() == k.rbegin(), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.crbegin() == k.crbegin(), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.rbegin() == k.rbegin(),
+                                      "[except.112] cannot compare iterators of different containers");
+                    CHECK_THROWS_WITH(j.crbegin() == k.crbegin(),
+                                      "[except.112] cannot compare iterators of different containers");
 
-                    CHECK_THROWS_AS(j.rbegin() < k.rbegin(), std::domain_error);
-                    CHECK_THROWS_AS(j.crbegin() < k.crbegin(), std::domain_error);
-                    CHECK_THROWS_WITH(j.rbegin() < k.rbegin(), "cannot compare iterators of different containers");
-                    CHECK_THROWS_WITH(j.crbegin() < k.crbegin(), "cannot compare iterators of different containers");
+                    CHECK_THROWS_AS(j.rbegin() < k.rbegin(), json::invalid_iterator);
+                    CHECK_THROWS_AS(j.crbegin() < k.crbegin(), json::invalid_iterator);
+                    CHECK_THROWS_WITH(j.rbegin() < k.rbegin(),
+                                      "[except.112] cannot compare iterators of different containers");
+                    CHECK_THROWS_WITH(j.crbegin() < k.crbegin(),
+                                      "[except.112] cannot compare iterators of different containers");
                 }
             }
         }
@@ -7152,53 +7331,53 @@
             {
                 {
                     auto it = j_object.rbegin();
-                    CHECK_THROWS_AS(it += 1, std::domain_error);
-                    CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it += 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it += 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.crbegin();
-                    CHECK_THROWS_AS(it += 1, std::domain_error);
-                    CHECK_THROWS_WITH(it += 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it += 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it += 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.rbegin();
-                    CHECK_THROWS_AS(it + 1, std::domain_error);
-                    CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it + 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it + 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.crbegin();
-                    CHECK_THROWS_AS(it + 1, std::domain_error);
-                    CHECK_THROWS_WITH(it + 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it + 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it + 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.rbegin();
-                    CHECK_THROWS_AS(it -= 1, std::domain_error);
-                    CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it -= 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it -= 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.crbegin();
-                    CHECK_THROWS_AS(it -= 1, std::domain_error);
-                    CHECK_THROWS_WITH(it -= 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it -= 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it -= 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.rbegin();
-                    CHECK_THROWS_AS(it - 1, std::domain_error);
-                    CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.crbegin();
-                    CHECK_THROWS_AS(it - 1, std::domain_error);
-                    CHECK_THROWS_WITH(it - 1, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - 1, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - 1, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.rbegin();
-                    CHECK_THROWS_AS(it - it, std::domain_error);
-                    CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - it, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - it, "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.crbegin();
-                    CHECK_THROWS_AS(it - it, std::domain_error);
-                    CHECK_THROWS_WITH(it - it, "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it - it, json::invalid_iterator);
+                    CHECK_THROWS_WITH(it - it, "[except.109] cannot use offsets with object iterators");
                 }
             }
 
@@ -7281,17 +7460,17 @@
             {
                 {
                     auto it = j_object.rbegin();
-                    CHECK_THROWS_AS(it[0], std::domain_error);
-                    CHECK_THROWS_AS(it[1], std::domain_error);
-                    CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators");
-                    CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.109] cannot use offsets with object iterators");
+                    CHECK_THROWS_WITH(it[1], "[except.109] cannot use offsets with object iterators");
                 }
                 {
                     auto it = j_object.crbegin();
-                    CHECK_THROWS_AS(it[0], std::domain_error);
-                    CHECK_THROWS_AS(it[1], std::domain_error);
-                    CHECK_THROWS_WITH(it[0], "cannot use offsets with object iterators");
-                    CHECK_THROWS_WITH(it[1], "cannot use offsets with object iterators");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.109] cannot use offsets with object iterators");
+                    CHECK_THROWS_WITH(it[1], "[except.109] cannot use offsets with object iterators");
                 }
             }
 
@@ -7321,17 +7500,17 @@
             {
                 {
                     auto it = j_null.rbegin();
-                    CHECK_THROWS_AS(it[0], std::out_of_range);
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[0], "cannot get value");
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.114] cannot get value");
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
                 {
                     auto it = j_null.crbegin();
-                    CHECK_THROWS_AS(it[0], std::out_of_range);
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[0], "cannot get value");
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[0], json::invalid_iterator);
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[0], "[except.114] cannot get value");
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
             }
 
@@ -7340,14 +7519,14 @@
                 {
                     auto it = j_value.rbegin();
                     CHECK(it[0] == json(42));
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
                 {
                     auto it = j_value.crbegin();
                     CHECK(it[0] == json(42));
-                    CHECK_THROWS_AS(it[1], std::out_of_range);
-                    CHECK_THROWS_WITH(it[1], "cannot get value");
+                    CHECK_THROWS_AS(it[1], json::invalid_iterator);
+                    CHECK_THROWS_WITH(it[1], "[except.114] cannot get value");
                 }
             }
         }
@@ -8005,8 +8184,8 @@
                 SECTION("other type")
                 {
                     json j = 1;
-                    CHECK_THROWS_AS(j.push_back("Hello"), std::domain_error);
-                    CHECK_THROWS_WITH(j.push_back("Hello"), "cannot use push_back() with number");
+                    CHECK_THROWS_AS(j.push_back("Hello"), json::type_error);
+                    CHECK_THROWS_WITH(j.push_back("Hello"), "[except.307] cannot use push_back() with number");
                 }
             }
 
@@ -8035,8 +8214,8 @@
                 {
                     json j = 1;
                     json k("Hello");
-                    CHECK_THROWS_AS(j.push_back(k), std::domain_error);
-                    CHECK_THROWS_WITH(j.push_back(k), "cannot use push_back() with number");
+                    CHECK_THROWS_AS(j.push_back(k), json::type_error);
+                    CHECK_THROWS_WITH(j.push_back(k), "[except.307] cannot use push_back() with number");
                 }
             }
         }
@@ -8068,9 +8247,9 @@
             {
                 json j = 1;
                 json k("Hello");
-                CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), std::domain_error);
+                CHECK_THROWS_AS(j.push_back(json::object_t::value_type({"one", 1})), json::type_error);
                 CHECK_THROWS_WITH(j.push_back(json::object_t::value_type({"one", 1})),
-                                  "cannot use push_back() with number");
+                                  "[except.307] cannot use push_back() with number");
             }
         }
 
@@ -8105,8 +8284,8 @@
                 CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
 
                 json k = {{"key1", 1}};
-                CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), std::domain_error);
-                CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "cannot use push_back() with object");
+                CHECK_THROWS_AS(k.push_back({1, 2, 3, 4}), json::type_error);
+                CHECK_THROWS_WITH(k.push_back({1, 2, 3, 4}), "[except.307] cannot use push_back() with object");
             }
         }
     }
@@ -8137,8 +8316,8 @@
                 SECTION("other type")
                 {
                     json j = 1;
-                    CHECK_THROWS_AS(j += "Hello", std::domain_error);
-                    CHECK_THROWS_WITH(j += "Hello", "cannot use push_back() with number");
+                    CHECK_THROWS_AS(j += "Hello", json::type_error);
+                    CHECK_THROWS_WITH(j += "Hello", "[except.307] cannot use push_back() with number");
                 }
             }
 
@@ -8167,8 +8346,8 @@
                 {
                     json j = 1;
                     json k("Hello");
-                    CHECK_THROWS_AS(j += k, std::domain_error);
-                    CHECK_THROWS_WITH(j += k, "cannot use push_back() with number");
+                    CHECK_THROWS_AS(j += k, json::type_error);
+                    CHECK_THROWS_WITH(j += k, "[except.307] cannot use push_back() with number");
                 }
             }
         }
@@ -8200,9 +8379,9 @@
             {
                 json j = 1;
                 json k("Hello");
-                CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), std::domain_error);
+                CHECK_THROWS_AS(j += json::object_t::value_type({"one", 1}), json::type_error);
                 CHECK_THROWS_WITH(j += json::object_t::value_type({"one", 1}),
-                                  "cannot use push_back() with number");
+                                  "[except.307] cannot use push_back() with number");
             }
         }
 
@@ -8237,8 +8416,8 @@
                 CHECK(j == json({{"key1", 1}, {"key2", "bar"}}));
 
                 json k = {{"key1", 1}};
-                CHECK_THROWS_AS((k += {1, 2, 3, 4}), std::domain_error);
-                CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "cannot use push_back() with object");
+                CHECK_THROWS_AS((k += {1, 2, 3, 4}), json::type_error);
+                CHECK_THROWS_WITH((k += {1, 2, 3, 4}), "[except.307] cannot use push_back() with object");
             }
         }
     }
@@ -8372,14 +8551,15 @@
             {
                 json j_other_array2 = {"first", "second"};
 
-                CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()), std::domain_error);
+                CHECK_THROWS_AS(j_array.insert(j_array.end(), j_array.begin(), j_array.end()),
+                                json::invalid_iterator);
                 CHECK_THROWS_AS(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()),
-                                std::domain_error);
+                                json::invalid_iterator);
 
                 CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_array.begin(), j_array.end()),
-                                  "passed iterators may not belong to container");
+                                  "[except.111] passed iterators may not belong to container");
                 CHECK_THROWS_WITH(j_array.insert(j_array.end(), j_other_array.begin(), j_other_array2.end()),
-                                  "iterators do not fit");
+                                  "[except.110] iterators do not fit");
             }
         }
 
@@ -8418,22 +8598,24 @@
             // pass iterator to a different array
             json j_another_array = {1, 2};
             json j_yet_another_array = {"first", "second"};
-            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), std::domain_error);
-            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), std::domain_error);
-            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), std::domain_error);
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10), json::invalid_iterator);
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_value), json::invalid_iterator);
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), 10, 11), json::invalid_iterator);
             CHECK_THROWS_AS(j_array.insert(j_another_array.end(), j_yet_another_array.begin(),
-                                           j_yet_another_array.end()), std::domain_error);
-            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), std::domain_error);
+                                           j_yet_another_array.end()), json::invalid_iterator);
+            CHECK_THROWS_AS(j_array.insert(j_another_array.end(), {1, 2, 3, 4}), json::invalid_iterator);
 
-            CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10), "iterator does not fit current value");
+            CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10),
+                              "[except.103] iterator does not fit current value");
             CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_value),
-                              "iterator does not fit current value");
+                              "[except.103] iterator does not fit current value");
             CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), 10, 11),
-                              "iterator does not fit current value");
+                              "[except.103] iterator does not fit current value");
             CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), j_yet_another_array.begin(),
-                                             j_yet_another_array.end()), "iterator does not fit current value");
+                                             j_yet_another_array.end()),
+                              "[except.103] iterator does not fit current value");
             CHECK_THROWS_WITH(j_array.insert(j_another_array.end(), {1, 2, 3, 4}),
-                              "iterator does not fit current value");
+                              "[except.103] iterator does not fit current value");
         }
 
         SECTION("non-array type")
@@ -8441,20 +8623,24 @@
             // call insert on a non-array type
             json j_nonarray = 3;
             json j_yet_another_array = {"first", "second"};
-            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), std::domain_error);
-            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), std::domain_error);
-            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), std::domain_error);
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10), json::type_error);
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_value), json::type_error);
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), 10, 11), json::type_error);
             CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(),
-                                              j_yet_another_array.end()), std::domain_error);
-            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), std::domain_error);
+                                              j_yet_another_array.end()), json::type_error);
+            CHECK_THROWS_AS(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}), json::type_error);
 
-            CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10), "cannot use insert() with number");
-            CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value), "cannot use insert() with number");
-            CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11), "cannot use insert() with number");
+            CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10),
+                              "[except.309] cannot use insert() with number");
+            CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_value),
+                              "[except.309] cannot use insert() with number");
+            CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), 10, 11),
+                              "[except.309] cannot use insert() with number");
             CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), j_yet_another_array.begin(),
-                                                j_yet_another_array.end()), "cannot use insert() with number");
+                                                j_yet_another_array.end()),
+                              "[except.309] cannot use insert() with number");
             CHECK_THROWS_WITH(j_nonarray.insert(j_nonarray.end(), {1, 2, 3, 4}),
-                              "cannot use insert() with number");
+                              "[except.309] cannot use insert() with number");
         }
     }
 
@@ -8506,8 +8692,8 @@
                 json j = 17;
                 json::array_t a = {"foo", "bar", "baz"};
 
-                CHECK_THROWS_AS(j.swap(a), std::domain_error);
-                CHECK_THROWS_WITH(j.swap(a), "cannot use swap() with number");
+                CHECK_THROWS_AS(j.swap(a), json::type_error);
+                CHECK_THROWS_WITH(j.swap(a), "[except.308] cannot use swap() with number");
             }
         }
 
@@ -8532,8 +8718,8 @@
                 json j = 17;
                 json::object_t o = {{"cow", "Kuh"}, {"chicken", "Huhn"}};
 
-                CHECK_THROWS_AS(j.swap(o), std::domain_error);
-                CHECK_THROWS_WITH(j.swap(o), "cannot use swap() with number");
+                CHECK_THROWS_AS(j.swap(o), json::type_error);
+                CHECK_THROWS_WITH(j.swap(o), "[except.308] cannot use swap() with number");
             }
         }
 
@@ -8558,8 +8744,8 @@
                 json j = 17;
                 json::string_t s = "Hallo Welt";
 
-                CHECK_THROWS_AS(j.swap(s), std::domain_error);
-                CHECK_THROWS_WITH(j.swap(s), "cannot use swap() with number");
+                CHECK_THROWS_AS(j.swap(s), json::type_error);
+                CHECK_THROWS_WITH(j.swap(s), "[except.308] cannot use swap() with number");
             }
         }
     }
@@ -8963,8 +9149,8 @@
             {
                 json j(json::value_t::null);
                 json::iterator it = j.begin();
-                CHECK_THROWS_AS(*it, std::out_of_range);
-                CHECK_THROWS_WITH(*it, "cannot get value");
+                CHECK_THROWS_AS(*it, json::invalid_iterator);
+                CHECK_THROWS_WITH(*it, "[except.114] cannot get value");
             }
 
             SECTION("number")
@@ -8973,8 +9159,8 @@
                 json::iterator it = j.begin();
                 CHECK(*it == json(17));
                 it = j.end();
-                CHECK_THROWS_AS(*it, std::out_of_range);
-                CHECK_THROWS_WITH(*it, "cannot get value");
+                CHECK_THROWS_AS(*it, json::invalid_iterator);
+                CHECK_THROWS_WITH(*it, "[except.114] cannot get value");
             }
 
             SECTION("object")
@@ -8998,8 +9184,8 @@
             {
                 json j(json::value_t::null);
                 json::iterator it = j.begin();
-                CHECK_THROWS_AS(it->type_name(), std::out_of_range);
-                CHECK_THROWS_WITH(it->type_name(), "cannot get value");
+                CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it->type_name(), "[except.114] cannot get value");
             }
 
             SECTION("number")
@@ -9008,8 +9194,8 @@
                 json::iterator it = j.begin();
                 CHECK(it->type_name() == "number");
                 it = j.end();
-                CHECK_THROWS_AS(it->type_name(), std::out_of_range);
-                CHECK_THROWS_WITH(it->type_name(), "cannot get value");
+                CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it->type_name(), "[except.114] cannot get value");
             }
 
             SECTION("object")
@@ -9331,8 +9517,8 @@
             {
                 json j(json::value_t::null);
                 json::const_iterator it = j.cbegin();
-                CHECK_THROWS_AS(*it, std::out_of_range);
-                CHECK_THROWS_WITH(*it, "cannot get value");
+                CHECK_THROWS_AS(*it, json::invalid_iterator);
+                CHECK_THROWS_WITH(*it, "[except.114] cannot get value");
             }
 
             SECTION("number")
@@ -9341,8 +9527,8 @@
                 json::const_iterator it = j.cbegin();
                 CHECK(*it == json(17));
                 it = j.cend();
-                CHECK_THROWS_AS(*it, std::out_of_range);
-                CHECK_THROWS_WITH(*it, "cannot get value");
+                CHECK_THROWS_AS(*it, json::invalid_iterator);
+                CHECK_THROWS_WITH(*it, "[except.114] cannot get value");
             }
 
             SECTION("object")
@@ -9366,8 +9552,8 @@
             {
                 json j(json::value_t::null);
                 json::const_iterator it = j.cbegin();
-                CHECK_THROWS_AS(it->type_name(), std::out_of_range);
-                CHECK_THROWS_WITH(it->type_name(), "cannot get value");
+                CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it->type_name(), "[except.114] cannot get value");
             }
 
             SECTION("number")
@@ -9376,8 +9562,8 @@
                 json::const_iterator it = j.cbegin();
                 CHECK(it->type_name() == "number");
                 it = j.cend();
-                CHECK_THROWS_AS(it->type_name(), std::out_of_range);
-                CHECK_THROWS_WITH(it->type_name(), "cannot get value");
+                CHECK_THROWS_AS(it->type_name(), json::invalid_iterator);
+                CHECK_THROWS_WITH(it->type_name(), "[except.114] cannot get value");
             }
 
             SECTION("object")
@@ -9774,9 +9960,11 @@
 
     SECTION("to_unicode")
     {
-        CHECK(json::lexer::to_unicode(0x1F4A9) == "💩");
-        CHECK_THROWS_AS(json::lexer::to_unicode(0x200000), std::out_of_range);
-        CHECK_THROWS_WITH(json::lexer::to_unicode(0x200000), "code points above 0x10FFFF are invalid");
+        json::lexer l("");
+        CHECK(l.to_unicode(0x1F4A9) == "💩");
+        CHECK_THROWS_AS(l.to_unicode(0x200000), json::parse_error);
+        CHECK_THROWS_WITH(l.to_unicode(0x200000),
+                          "[except.203] parse error: unexpected end of input at byte 1; code points above 0x10FFFF are invalid");
     }
 }
 
@@ -9835,51 +10023,55 @@
             SECTION("errors")
             {
                 // error: tab in string
-                CHECK_THROWS_AS(json::parser("\"\t\"").parse(), std::invalid_argument);
-                CHECK_THROWS_WITH(json::parser("\"\t\"").parse(), "parse error - unexpected '\"'");
+                CHECK_THROWS_AS(json::parser("\"\t\"").parse(), json::parse_error);
+                CHECK_THROWS_WITH(json::parser("\"\t\"").parse(),
+                                  "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
                 // error: newline in string
-                CHECK_THROWS_AS(json::parser("\"\n\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\r\"").parse(), std::invalid_argument);
-                CHECK_THROWS_WITH(json::parser("\"\n\"").parse(), "parse error - unexpected '\"'");
-                CHECK_THROWS_WITH(json::parser("\"\r\"").parse(), "parse error - unexpected '\"'");
+                CHECK_THROWS_AS(json::parser("\"\n\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\r\"").parse(), json::parse_error);
+                CHECK_THROWS_WITH(json::parser("\"\n\"").parse(),
+                                  "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
+                CHECK_THROWS_WITH(json::parser("\"\r\"").parse(),
+                                  "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
                 // error: backspace in string
-                CHECK_THROWS_AS(json::parser("\"\b\"").parse(), std::invalid_argument);
-                CHECK_THROWS_WITH(json::parser("\"\b\"").parse(), "parse error - unexpected '\"'");
+                CHECK_THROWS_AS(json::parser("\"\b\"").parse(), json::parse_error);
+                CHECK_THROWS_WITH(json::parser("\"\b\"").parse(),
+                                  "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
                 // improve code coverage
-                CHECK_THROWS_AS(json::parser("\uFF01").parse(), std::invalid_argument);
+                CHECK_THROWS_AS(json::parser("\uFF01").parse(), json::parse_error);
                 // unescaped control characters
-                CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), std::invalid_argument);
+                CHECK_THROWS_AS(json::parser("\"\x00\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x01\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x02\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x03\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x04\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x05\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x06\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x07\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x08\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x09\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x0a\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x0b\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x0c\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x0d\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x0e\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x0f\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x10\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x11\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x12\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x13\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x14\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x15\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x16\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x17\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x18\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x19\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x1a\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x1b\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x1c\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x1d\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x1e\"").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("\"\x1f\"").parse(), json::parse_error);
             }
 
             SECTION("escaped")
@@ -10017,58 +10209,59 @@
 
             SECTION("invalid numbers")
             {
-                CHECK_THROWS_AS(json::parser("01").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("--1").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("1.").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("1E").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("1E-").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("1.E1").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-1E").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0E#").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0E-#").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0#").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0.0:").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0.0Z").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0E123:").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0e0-:").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0e-:").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("-0f").parse(), std::invalid_argument);
+                CHECK_THROWS_AS(json::parser("01").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("--1").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("1.").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("1E").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("1E-").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("1.E1").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-1E").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0E#").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0E-#").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0#").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0.0:").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0.0Z").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0E123:").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0e0-:").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0e-:").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("-0f").parse(), json::parse_error);
 
                 // numbers must not begin with "+"
-                CHECK_THROWS_AS(json::parser("+1").parse(), std::invalid_argument);
-                CHECK_THROWS_AS(json::parser("+0").parse(), std::invalid_argument);
+                CHECK_THROWS_AS(json::parser("+1").parse(), json::parse_error);
+                CHECK_THROWS_AS(json::parser("+0").parse(), json::parse_error);
 
                 CHECK_THROWS_WITH(json::parser("01").parse(),
-                                  "parse error - unexpected number literal; expected end of input");
-                CHECK_THROWS_WITH(json::parser("--1").parse(), "parse error - unexpected '-'");
+                                  "[except.201] parse error: unexpected '1' at byte 2; expected end of input");
+                CHECK_THROWS_WITH(json::parser("--1").parse(),
+                                  "[except.201] parse error: unexpected '-' at byte 1; expected JSON value");
                 CHECK_THROWS_WITH(json::parser("1.").parse(),
-                                  "parse error - unexpected '.'; expected end of input");
+                                  "[except.201] parse error: unexpected '.' at byte 2; expected end of input");
                 CHECK_THROWS_WITH(json::parser("1E").parse(),
-                                  "parse error - unexpected 'E'; expected end of input");
+                                  "[except.201] parse error: unexpected 'E' at byte 2; expected end of input");
                 CHECK_THROWS_WITH(json::parser("1E-").parse(),
-                                  "parse error - unexpected 'E'; expected end of input");
+                                  "[except.201] parse error: unexpected 'E' at byte 2; expected end of input");
                 CHECK_THROWS_WITH(json::parser("1.E1").parse(),
-                                  "parse error - unexpected '.'; expected end of input");
+                                  "[except.201] parse error: unexpected '.' at byte 2; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-1E").parse(),
-                                  "parse error - unexpected 'E'; expected end of input");
+                                  "[except.201] parse error: unexpected 'E' at byte 3; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0E#").parse(),
-                                  "parse error - unexpected 'E'; expected end of input");
+                                  "[except.201] parse error: unexpected 'E' at byte 3; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0E-#").parse(),
-                                  "parse error - unexpected 'E'; expected end of input");
+                                  "[except.201] parse error: unexpected 'E' at byte 3; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0#").parse(),
-                                  "parse error - unexpected '#'; expected end of input");
+                                  "[except.201] parse error: unexpected '#' at byte 3; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0.0:").parse(),
-                                  "parse error - unexpected ':'; expected end of input");
+                                  "[except.201] parse error: unexpected ':' at byte 5; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0.0Z").parse(),
-                                  "parse error - unexpected 'Z'; expected end of input");
+                                  "[except.201] parse error: unexpected 'Z' at byte 5; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0E123:").parse(),
-                                  "parse error - unexpected ':'; expected end of input");
+                                  "[except.201] parse error: unexpected ':' at byte 7; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0e0-:").parse(),
-                                  "parse error - unexpected '-'; expected end of input");
+                                  "[except.201] parse error: unexpected '-' at byte 5; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0e-:").parse(),
-                                  "parse error - unexpected 'e'; expected end of input");
+                                  "[except.201] parse error: unexpected 'e' at byte 3; expected end of input");
                 CHECK_THROWS_WITH(json::parser("-0f").parse(),
-                                  "parse error - unexpected 'f'; expected end of input");
+                                  "[except.201] parse error: unexpected 'f' at byte 3; expected end of input");
             }
         }
     }
@@ -10076,135 +10269,150 @@
     SECTION("parse errors")
     {
         // unexpected end of number
-        CHECK_THROWS_AS(json::parser("0.").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("-").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("--").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("-0.").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("-.").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("-:").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("0.:").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("e.").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("1e.").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("1e/").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("1e:").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("1E.").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("1E/").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("1E:").parse(), std::invalid_argument);
+        CHECK_THROWS_AS(json::parser("0.").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("-").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("--").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("-0.").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("-.").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("-:").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("0.:").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("e.").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("1e.").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("1e/").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("1e:").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("1E.").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("1E/").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("1E:").parse(), json::parse_error);
         CHECK_THROWS_WITH(json::parser("0.").parse(),
-                          "parse error - unexpected '.'; expected end of input");
-        CHECK_THROWS_WITH(json::parser("-").parse(), "parse error - unexpected '-'");
+                          "[except.201] parse error: unexpected '.' at byte 2; expected end of input");
+        CHECK_THROWS_WITH(json::parser("-").parse(),
+                          "[except.201] parse error: unexpected '-' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("--").parse(),
-                          "parse error - unexpected '-'");
+                          "[except.201] parse error: unexpected '-' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("-0.").parse(),
-                          "parse error - unexpected '.'; expected end of input");
+                          "[except.201] parse error: unexpected '.' at byte 3; expected end of input");
         CHECK_THROWS_WITH(json::parser("-.").parse(),
-                          "parse error - unexpected '-'");
+                          "[except.201] parse error: unexpected '-' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("-:").parse(),
-                          "parse error - unexpected '-'");
+                          "[except.201] parse error: unexpected '-' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("0.:").parse(),
-                          "parse error - unexpected '.'; expected end of input");
+                          "[except.201] parse error: unexpected '.' at byte 2; expected end of input");
         CHECK_THROWS_WITH(json::parser("e.").parse(),
-                          "parse error - unexpected 'e'");
+                          "[except.201] parse error: unexpected 'e' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("1e.").parse(),
-                          "parse error - unexpected 'e'; expected end of input");
+                          "[except.201] parse error: unexpected 'e' at byte 2; expected end of input");
         CHECK_THROWS_WITH(json::parser("1e/").parse(),
-                          "parse error - unexpected 'e'; expected end of input");
+                          "[except.201] parse error: unexpected 'e' at byte 2; expected end of input");
         CHECK_THROWS_WITH(json::parser("1e:").parse(),
-                          "parse error - unexpected 'e'; expected end of input");
+                          "[except.201] parse error: unexpected 'e' at byte 2; expected end of input");
         CHECK_THROWS_WITH(json::parser("1E.").parse(),
-                          "parse error - unexpected 'E'; expected end of input");
+                          "[except.201] parse error: unexpected 'E' at byte 2; expected end of input");
         CHECK_THROWS_WITH(json::parser("1E/").parse(),
-                          "parse error - unexpected 'E'; expected end of input");
+                          "[except.201] parse error: unexpected 'E' at byte 2; expected end of input");
         CHECK_THROWS_WITH(json::parser("1E:").parse(),
-                          "parse error - unexpected 'E'; expected end of input");
+                          "[except.201] parse error: unexpected 'E' at byte 2; expected end of input");
 
         // unexpected end of null
-        CHECK_THROWS_AS(json::parser("n").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("nu").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("nul").parse(), std::invalid_argument);
-        CHECK_THROWS_WITH(json::parser("n").parse(), "parse error - unexpected 'n'");
+        CHECK_THROWS_AS(json::parser("n").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("nu").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("nul").parse(), json::parse_error);
+        CHECK_THROWS_WITH(json::parser("n").parse(),
+                          "[except.201] parse error: unexpected 'n' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("nu").parse(),
-                          "parse error - unexpected 'n'");
+                          "[except.201] parse error: unexpected 'n' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("nul").parse(),
-                          "parse error - unexpected 'n'");
+                          "[except.201] parse error: unexpected 'n' at byte 1; expected JSON value");
 
         // unexpected end of true
-        CHECK_THROWS_AS(json::parser("t").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("tr").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("tru").parse(), std::invalid_argument);
-        CHECK_THROWS_WITH(json::parser("t").parse(), "parse error - unexpected 't'");
+        CHECK_THROWS_AS(json::parser("t").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("tr").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("tru").parse(), json::parse_error);
+        CHECK_THROWS_WITH(json::parser("t").parse(),
+                          "[except.201] parse error: unexpected 't' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("tr").parse(),
-                          "parse error - unexpected 't'");
+                          "[except.201] parse error: unexpected 't' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("tru").parse(),
-                          "parse error - unexpected 't'");
+                          "[except.201] parse error: unexpected 't' at byte 1; expected JSON value");
 
         // unexpected end of false
-        CHECK_THROWS_AS(json::parser("f").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("fa").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("fal").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("fals").parse(), std::invalid_argument);
-        CHECK_THROWS_WITH(json::parser("f").parse(), "parse error - unexpected 'f'");
+        CHECK_THROWS_AS(json::parser("f").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("fa").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("fal").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("fals").parse(), json::parse_error);
+        CHECK_THROWS_WITH(json::parser("f").parse(),
+                          "[except.201] parse error: unexpected 'f' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("fa").parse(),
-                          "parse error - unexpected 'f'");
+                          "[except.201] parse error: unexpected 'f' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("fal").parse(),
-                          "parse error - unexpected 'f'");
+                          "[except.201] parse error: unexpected 'f' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("fals").parse(),
-                          "parse error - unexpected 'f'");
+                          "[except.201] parse error: unexpected 'f' at byte 1; expected JSON value");
 
         // missing/unexpected end of array
-        CHECK_THROWS_AS(json::parser("[").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("[1").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("[1,").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("[1,]").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("]").parse(), std::invalid_argument);
+        CHECK_THROWS_AS(json::parser("[").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("[,").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("[1").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("[1,").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("[1,]").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("]").parse(), json::parse_error);
         CHECK_THROWS_WITH(json::parser("[").parse(),
-                          "parse error - unexpected end of input");
+                          "[except.201] parse error: unexpected end of input at byte 2; expected JSON value");
+        CHECK_THROWS_WITH(json::parser("[,").parse(),
+                          "[except.201] parse error: unexpected ',' at byte 2; expected JSON value");
         CHECK_THROWS_WITH(json::parser("[1").parse(),
-                          "parse error - unexpected end of input; expected ']'");
+                          "[except.201] parse error: unexpected end of input at byte 3; expected ']'");
         CHECK_THROWS_WITH(json::parser("[1,").parse(),
-                          "parse error - unexpected end of input");
+                          "[except.201] parse error: unexpected end of input at byte 4; expected JSON value");
         CHECK_THROWS_WITH(json::parser("[1,]").parse(),
-                          "parse error - unexpected ']'");
-        CHECK_THROWS_WITH(json::parser("]").parse(), "parse error - unexpected ']'");
+                          "[except.201] parse error: unexpected ']' at byte 4; expected JSON value");
+        CHECK_THROWS_WITH(json::parser("]").parse(),
+                          "[except.201] parse error: unexpected ']' at byte 1; expected JSON value");
 
         // missing/unexpected end of object
-        CHECK_THROWS_AS(json::parser("{").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("}").parse(), std::invalid_argument);
+        CHECK_THROWS_AS(json::parser("{").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("{,").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("{\"foo\"").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("{\"foo\":").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("{\"foo\":}").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("{\"foo\":1").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("{\"foo\":1,}").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("}").parse(), json::parse_error);
         CHECK_THROWS_WITH(json::parser("{").parse(),
-                          "parse error - unexpected end of input; expected string literal");
+                          "[except.201] parse error: unexpected end of input at byte 2; expected string literal");
+        CHECK_THROWS_WITH(json::parser("{,").parse(),
+                          "[except.201] parse error: unexpected ',' at byte 2; expected JSON value");
         CHECK_THROWS_WITH(json::parser("{\"foo\"").parse(),
-                          "parse error - unexpected end of input; expected ':'");
+                          "[except.201] parse error: unexpected end of input at byte 7; expected ':'");
         CHECK_THROWS_WITH(json::parser("{\"foo\":").parse(),
-                          "parse error - unexpected end of input");
+                          "[except.201] parse error: unexpected end of input at byte 8; expected JSON value");
         CHECK_THROWS_WITH(json::parser("{\"foo\":}").parse(),
-                          "parse error - unexpected '}'");
+                          "[except.201] parse error: unexpected '}' at byte 8; expected JSON value");
+        CHECK_THROWS_WITH(json::parser("{\"foo\":1").parse(),
+                          "[except.201] parse error: unexpected end of input at byte 9; expected '}'");
         CHECK_THROWS_WITH(json::parser("{\"foo\":1,}").parse(),
-                          "parse error - unexpected '}'; expected string literal");
-        CHECK_THROWS_WITH(json::parser("}").parse(), "parse error - unexpected '}'");
+                          "[except.201] parse error: unexpected '}' at byte 10; expected string literal");
+        CHECK_THROWS_WITH(json::parser("}").parse(),
+                          "[except.201] parse error: unexpected '}' at byte 1; expected JSON value");
 
         // missing/unexpected end of string
-        CHECK_THROWS_AS(json::parser("\"").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("\"\\\"").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), std::invalid_argument);
-        CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), std::invalid_argument);
+        CHECK_THROWS_AS(json::parser("\"").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("\"\\\"").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("\"\\u\"").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("\"\\u0\"").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("\"\\u01\"").parse(), json::parse_error);
+        CHECK_THROWS_AS(json::parser("\"\\u012\"").parse(), json::parse_error);
         CHECK_THROWS_WITH(json::parser("\"").parse(),
-                          "parse error - unexpected '\"'");
+                          "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("\"\\\"").parse(),
-                          "parse error - unexpected '\"'");
+                          "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("\"\\u\"").parse(),
-                          "parse error - unexpected '\"'");
+                          "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("\"\\u0\"").parse(),
-                          "parse error - unexpected '\"'");
+                          "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("\"\\u01\"").parse(),
-                          "parse error - unexpected '\"'");
+                          "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
         CHECK_THROWS_WITH(json::parser("\"\\u012\"").parse(),
-                          "parse error - unexpected '\"'");
+                          "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
 
         // invalid escapes
         for (int c = 1; c < 128; ++c)
@@ -10236,8 +10444,9 @@
                 // any other combination of backslash and character is invalid
                 default:
                 {
-                    CHECK_THROWS_AS(json::parser(s).parse(), std::invalid_argument);
-                    CHECK_THROWS_WITH(json::parser(s).parse(), "parse error - unexpected '\"'");
+                    CHECK_THROWS_AS(json::parser(s).parse(), json::parse_error);
+                    CHECK_THROWS_WITH(json::parser(s).parse(),
+                                      "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
                     break;
                 }
             }
@@ -10302,32 +10511,37 @@
                 }
                 else
                 {
-                    CHECK_THROWS_AS(json::parser(s1).parse(), std::invalid_argument);
-                    CHECK_THROWS_AS(json::parser(s2).parse(), std::invalid_argument);
-                    CHECK_THROWS_AS(json::parser(s3).parse(), std::invalid_argument);
-                    CHECK_THROWS_AS(json::parser(s4).parse(), std::invalid_argument);
+                    CHECK_THROWS_AS(json::parser(s1).parse(), json::parse_error);
+                    CHECK_THROWS_AS(json::parser(s2).parse(), json::parse_error);
+                    CHECK_THROWS_AS(json::parser(s3).parse(), json::parse_error);
+                    CHECK_THROWS_AS(json::parser(s4).parse(), json::parse_error);
 
-                    CHECK_THROWS_WITH(json::parser(s1).parse(), "parse error - unexpected '\"'");
-                    CHECK_THROWS_WITH(json::parser(s2).parse(), "parse error - unexpected '\"'");
-                    CHECK_THROWS_WITH(json::parser(s3).parse(), "parse error - unexpected '\"'");
-                    CHECK_THROWS_WITH(json::parser(s4).parse(), "parse error - unexpected '\"'");
+                    CHECK_THROWS_WITH(json::parser(s1).parse(),
+                                      "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
+                    CHECK_THROWS_WITH(json::parser(s2).parse(),
+                                      "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
+                    CHECK_THROWS_WITH(json::parser(s3).parse(),
+                                      "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
+                    CHECK_THROWS_WITH(json::parser(s4).parse(),
+                                      "[except.201] parse error: unexpected '\"' at byte 1; expected JSON value");
                 }
             }
         }
 
         // missing part of a surrogate pair
-        CHECK_THROWS_AS(json::parse("\"\\uD80C\""), std::invalid_argument);
-        CHECK_THROWS_WITH(json::parse("\"\\uD80C\""), "missing low surrogate");
+        CHECK_THROWS_AS(json::parse("\"\\uD80C\""), json::parse_error);
+        CHECK_THROWS_WITH(json::parse("\"\\uD80C\""),
+                          "[except.204] parse error: unexpected '\"\\uD80C\"' at byte 1; missing low surrogate");
         // invalid surrogate pair
-        CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), std::invalid_argument);
-        CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), std::invalid_argument);
-        CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), std::invalid_argument);
+        CHECK_THROWS_AS(json::parse("\"\\uD80C\\uD80C\""), json::parse_error);
+        CHECK_THROWS_AS(json::parse("\"\\uD80C\\u0000\""), json::parse_error);
+        CHECK_THROWS_AS(json::parse("\"\\uD80C\\uFFFF\""), json::parse_error);
         CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uD80C\""),
-                          "missing or wrong low surrogate");
+                          "[except.202] parse error: unexpected '\"\\uD80C\\uD80C\"' at byte 1; missing or wrong low surrogate");
         CHECK_THROWS_WITH(json::parse("\"\\uD80C\\u0000\""),
-                          "missing or wrong low surrogate");
+                          "[except.202] parse error: unexpected '\"\\uD80C\\u0000\"' at byte 1; missing or wrong low surrogate");
         CHECK_THROWS_WITH(json::parse("\"\\uD80C\\uFFFF\""),
-                          "missing or wrong low surrogate");
+                          "[except.202] parse error: unexpected '\"\\uD80C\\uFFFF\"' at byte 1; missing or wrong low surrogate");
     }
 
     SECTION("callback function")
@@ -10970,8 +11184,9 @@
             SECTION("sorting an object")
             {
                 json j({{"one", 1}, {"two", 2}});
-                CHECK_THROWS_AS(std::sort(j.begin(), j.end()), std::domain_error);
-                CHECK_THROWS_WITH(std::sort(j.begin(), j.end()), "cannot use offsets with object iterators");
+                CHECK_THROWS_AS(std::sort(j.begin(), j.end()), json::invalid_iterator);
+                CHECK_THROWS_WITH(std::sort(j.begin(), j.end()),
+                                  "[except.109] cannot use offsets with object iterators");
             }
         }
 
@@ -11927,7 +12142,7 @@
             CAPTURE(filename);
             json j;
             std::ifstream f(filename);
-            CHECK_THROWS_AS(j << f, std::invalid_argument);
+            CHECK_THROWS_AS(j << f, json::parse_error);
         }
     }
 
@@ -12321,6 +12536,9 @@
             // string to store the code point as unescaped character sequence
             std::string unescaped_string;
 
+            // a lexer to call "to_unicode"
+            json::lexer l("");
+
             if (cp < 0x10000u)
             {
                 // code points in the Basic Multilingual Plane can be
@@ -12334,7 +12552,7 @@
                 // they are checked with codepoint_to_unicode.
                 if (cp > 0x1f and cp != 0x22 and cp != 0x5c)
                 {
-                    unescaped_string = json::lexer::to_unicode(cp);
+                    unescaped_string = l.to_unicode(cp);
                 }
             }
             else
@@ -12346,7 +12564,7 @@
                 const auto codepoint2 = 0xdc00u + ((cp - 0x10000u) & 0x3ffu);
                 escaped_string = codepoint_to_unicode(codepoint1);
                 escaped_string += codepoint_to_unicode(codepoint2);
-                unescaped_string += json::lexer::to_unicode(codepoint1, codepoint2);
+                unescaped_string += l.to_unicode(codepoint1, codepoint2);
             }
 
             // all other code points are valid and must not yield parse errors
@@ -12419,8 +12637,8 @@
 
     SECTION("error for incomplete/wrong BOM")
     {
-        CHECK_THROWS_AS(json::parse("\xef\xbb"), std::invalid_argument);
-        CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), std::invalid_argument);
+        CHECK_THROWS_AS(json::parse("\xef\xbb"), json::parse_error);
+        CHECK_THROWS_AS(json::parse("\xef\xbb\xbb"), json::parse_error);
     }
 }
 
@@ -12428,20 +12646,23 @@
 {
     SECTION("errors")
     {
-        CHECK_THROWS_AS(json::json_pointer("foo"), std::domain_error);
-        CHECK_THROWS_WITH(json::json_pointer("foo"), "JSON pointer must be empty or begin with '/'");
+        CHECK_THROWS_AS(json::json_pointer("foo"), json::extension_error);
+        CHECK_THROWS_WITH(json::json_pointer("foo"),
+                          "[except.403] JSON pointer must be empty or begin with '/'");
 
-        CHECK_THROWS_AS(json::json_pointer("/~~"), std::domain_error);
-        CHECK_THROWS_WITH(json::json_pointer("/~~"), "escape error: '~' must be followed with '0' or '1'");
+        CHECK_THROWS_AS(json::json_pointer("/~~"), json::extension_error);
+        CHECK_THROWS_WITH(json::json_pointer("/~~"),
+                          "[except.404] escape error: '~' must be followed with '0' or '1'");
 
-        CHECK_THROWS_AS(json::json_pointer("/~"), std::domain_error);
-        CHECK_THROWS_WITH(json::json_pointer("/~"), "escape error: '~' must be followed with '0' or '1'");
+        CHECK_THROWS_AS(json::json_pointer("/~"), json::extension_error);
+        CHECK_THROWS_WITH(json::json_pointer("/~"),
+                          "[except.404] escape error: '~' must be followed with '0' or '1'");
 
         json::json_pointer p;
-        CHECK_THROWS_AS(p.top(), std::domain_error);
-        CHECK_THROWS_WITH(p.top(), "JSON pointer has no parent");
-        CHECK_THROWS_AS(p.pop_back(), std::domain_error);
-        CHECK_THROWS_WITH(p.pop_back(), "JSON pointer has no parent");
+        CHECK_THROWS_AS(p.top(), json::extension_error);
+        CHECK_THROWS_WITH(p.top(), "[except.401] JSON pointer has no parent");
+        CHECK_THROWS_AS(p.pop_back(), json::extension_error);
+        CHECK_THROWS_WITH(p.pop_back(), "[except.401] JSON pointer has no parent");
     }
 
     SECTION("examples from RFC 6901")
@@ -12501,17 +12722,20 @@
             CHECK(j[json::json_pointer("/m~0n")] == j["m~n"]);
 
             // unescaped access
-            CHECK_THROWS_AS(j[json::json_pointer("/a/b")], std::out_of_range);
-            CHECK_THROWS_WITH(j[json::json_pointer("/a/b")], "unresolved reference token 'b'");
+            CHECK_THROWS_AS(j[json::json_pointer("/a/b")], json::extension_error);
+            CHECK_THROWS_WITH(j[json::json_pointer("/a/b")],
+                              "[except.405] unresolved reference token 'b'");
             // "/a/b" works for JSON {"a": {"b": 42}}
             CHECK(json({{"a", {{"b", 42}}}})[json::json_pointer("/a/b")] == json(42));
 
             // unresolved access
             json j_primitive = 1;
-            CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range);
-            CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'");
-            CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range);
-            CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'");
+            CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::extension_error);
+            CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer],
+                              "[except.405] unresolved reference token 'foo'");
+            CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::extension_error);
+            CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer),
+                              "[except.405] unresolved reference token 'foo'");
         }
 
         SECTION("const access")
@@ -12574,10 +12798,12 @@
 
             // unresolved access
             const json j_primitive = 1;
-            CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], std::out_of_range);
-            CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer], "unresolved reference token 'foo'");
-            CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), std::out_of_range);
-            CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer), "unresolved reference token 'foo'");
+            CHECK_THROWS_AS(j_primitive["/foo"_json_pointer], json::extension_error);
+            CHECK_THROWS_WITH(j_primitive["/foo"_json_pointer],
+                              "[except.405] unresolved reference token 'foo'");
+            CHECK_THROWS_AS(j_primitive.at("/foo"_json_pointer), json::extension_error);
+            CHECK_THROWS_WITH(j_primitive.at("/foo"_json_pointer),
+                              "[except.405] unresolved reference token 'foo'");
         }
 
         SECTION("user-defined string literal")
@@ -12632,31 +12858,35 @@
             CHECK(j == json({1, 13, 3, 33, nullptr, 55}));
 
             // error with leading 0
-            CHECK_THROWS_AS(j["/01"_json_pointer], std::domain_error);
-            CHECK_THROWS_WITH(j["/01"_json_pointer], "array index must not begin with '0'");
-            CHECK_THROWS_AS(j_const["/01"_json_pointer], std::domain_error);
-            CHECK_THROWS_WITH(j_const["/01"_json_pointer], "array index must not begin with '0'");
-            CHECK_THROWS_AS(j.at("/01"_json_pointer), std::domain_error);
-            CHECK_THROWS_WITH(j.at("/01"_json_pointer), "array index must not begin with '0'");
-            CHECK_THROWS_AS(j_const.at("/01"_json_pointer), std::domain_error);
-            CHECK_THROWS_WITH(j_const.at("/01"_json_pointer), "array index must not begin with '0'");
+            CHECK_THROWS_AS(j["/01"_json_pointer], json::extension_error);
+            CHECK_THROWS_WITH(j["/01"_json_pointer], "[except.402] array index must not begin with '0'");
+            CHECK_THROWS_AS(j_const["/01"_json_pointer], json::extension_error);
+            CHECK_THROWS_WITH(j_const["/01"_json_pointer], "[except.402] array index must not begin with '0'");
+            CHECK_THROWS_AS(j.at("/01"_json_pointer), json::extension_error);
+            CHECK_THROWS_WITH(j.at("/01"_json_pointer), "[except.402] array index must not begin with '0'");
+            CHECK_THROWS_AS(j_const.at("/01"_json_pointer), json::extension_error);
+            CHECK_THROWS_WITH(j_const.at("/01"_json_pointer),
+                              "[except.402] array index must not begin with '0'");
 
             // error with incorrect numbers
-            CHECK_THROWS_AS(j["/one"_json_pointer] = 1, std::invalid_argument);
+            CHECK_THROWS_AS(j["/one"_json_pointer] = 1, json::extension_error);
+            CHECK_THROWS_WITH(j["/one"_json_pointer],
+                              "[except.402] array index is 'one', but must be a number");
 
             // assign to "-"
             j["/-"_json_pointer] = 99;
             CHECK(j == json({1, 13, 3, 33, nullptr, 55, 99}));
 
             // error when using "-" in const object
-            CHECK_THROWS_AS(j_const["/-"_json_pointer], std::out_of_range);
-            CHECK_THROWS_WITH(j_const["/-"_json_pointer], "array index '-' (3) is out of range");
+            CHECK_THROWS_AS(j_const["/-"_json_pointer], json::extension_error);
+            CHECK_THROWS_WITH(j_const["/-"_json_pointer], "[except.406] array index '-' (3) is out of range");
 
             // error when using "-" with at
-            CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range);
-            CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (7) is out of range");
-            CHECK_THROWS_AS(j_const.at("/-"_json_pointer), std::out_of_range);
-            CHECK_THROWS_WITH(j_const.at("/-"_json_pointer), "array index '-' (3) is out of range");
+            CHECK_THROWS_AS(j.at("/-"_json_pointer), json::extension_error);
+            CHECK_THROWS_WITH(j.at("/-"_json_pointer), "[except.406] array index '-' (7) is out of range");
+            CHECK_THROWS_AS(j_const.at("/-"_json_pointer), json::extension_error);
+            CHECK_THROWS_WITH(j_const.at("/-"_json_pointer),
+                              "[except.406] array index '-' (3) is out of range");
         }
 
         SECTION("const access")
@@ -12677,10 +12907,10 @@
             CHECK_THROWS_WITH(j.at("/5"_json_pointer), "array index 5 is out of range");
 
             // assign to "-"
-            CHECK_THROWS_AS(j["/-"_json_pointer], std::out_of_range);
-            CHECK_THROWS_WITH(j["/-"_json_pointer], "array index '-' (3) is out of range");
-            CHECK_THROWS_AS(j.at("/-"_json_pointer), std::out_of_range);
-            CHECK_THROWS_WITH(j.at("/-"_json_pointer), "array index '-' (3) is out of range");
+            CHECK_THROWS_AS(j["/-"_json_pointer], json::extension_error);
+            CHECK_THROWS_WITH(j["/-"_json_pointer], "[except.406] array index '-' (3) is out of range");
+            CHECK_THROWS_AS(j.at("/-"_json_pointer), json::extension_error);
+            CHECK_THROWS_WITH(j.at("/-"_json_pointer), "[except.406] array index '-' (3) is out of range");
         }
 
     }
@@ -12736,17 +12966,18 @@
         CHECK(j_flatten.unflatten() == j);
 
         // error for nonobjects
-        CHECK_THROWS_AS(json(1).unflatten(), std::domain_error);
-        CHECK_THROWS_WITH(json(1).unflatten(), "only objects can be unflattened");
+        CHECK_THROWS_AS(json(1).unflatten(), json::extension_error);
+        CHECK_THROWS_WITH(json(1).unflatten(), "[except.412] only objects can be unflattened");
 
         // error for nonprimitve values
-        CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), std::domain_error);
-        CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(), "values in object must be primitive");
+        CHECK_THROWS_AS(json({{"/1", {1, 2, 3}}}).unflatten(), json::extension_error);
+        CHECK_THROWS_WITH(json({{"/1", {1, 2, 3}}}).unflatten(),
+        "[except.413] values in object must be primitive");
 
         // error for conflicting values
         json j_error = {{"", 42}, {"/foo", 17}};
-        CHECK_THROWS_AS(j_error.unflatten(), std::domain_error);
-        CHECK_THROWS_WITH(j_error.unflatten(), "invalid value to unflatten");
+        CHECK_THROWS_AS(j_error.unflatten(), json::extension_error);
+        CHECK_THROWS_WITH(j_error.unflatten(), "[except.411] invalid value to unflatten");
 
         // explicit roundtrip check
         CHECK(j.flatten().unflatten() == j);
@@ -13084,8 +13315,8 @@
                 )"_json;
 
             // check that evaluation throws
-            CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
-            CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
+            CHECK_THROWS_AS(doc.patch(patch), json::extension_error);
+            CHECK_THROWS_WITH(doc.patch(patch), "[except.410] unsuccessful: " + patch[0].dump());
         }
 
         SECTION("A.10. Adding a Nested Member Object")
@@ -13224,8 +13455,8 @@
                 )"_json;
 
             // check that evaluation throws
-            CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
-            CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
+            CHECK_THROWS_AS(doc.patch(patch), json::extension_error);
+            CHECK_THROWS_WITH(doc.patch(patch), "[except.410] unsuccessful: " + patch[0].dump());
         }
 
         SECTION("A.16. Adding an Array Value")
@@ -13414,40 +13645,40 @@
             {
                 json j;
                 json patch = {{"op", "add"}, {"path", ""}, {"value", 1}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.407] JSON patch must be an array of objects");
             }
 
             SECTION("not an array of objects")
             {
                 json j;
                 json patch = {"op", "add", "path", "", "value", 1};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "JSON patch must be an array of objects");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.407] JSON patch must be an array of objects");
             }
 
             SECTION("missing 'op'")
             {
                 json j;
                 json patch = {{{"foo", "bar"}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation must have member 'op'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation must have string member 'op'");
             }
 
             SECTION("non-string 'op'")
             {
                 json j;
                 json patch = {{{"op", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation must have string member 'op'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation must have string member 'op'");
             }
 
             SECTION("invalid operation")
             {
                 json j;
                 json patch = {{{"op", "foo"}, {"path", ""}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation value 'foo' is invalid");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.409] operation value 'foo' is invalid");
             }
         }
 
@@ -13457,32 +13688,32 @@
             {
                 json j;
                 json patch = {{{"op", "add"}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'add' must have string member 'path'");
             }
 
             SECTION("non-string 'path'")
             {
                 json j;
                 json patch = {{{"op", "add"}, {"path", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have string member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'add' must have string member 'path'");
             }
 
             SECTION("missing 'value'")
             {
                 json j;
                 json patch = {{{"op", "add"}, {"path", ""}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'add' must have member 'value'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'add' must have string member 'value'");
             }
 
             SECTION("invalid array index")
             {
                 json j = {1, 2};
                 json patch = {{{"op", "add"}, {"path", "/4"}, {"value", 4}}};
-                CHECK_THROWS_AS(j.patch(patch), std::out_of_range);
-                CHECK_THROWS_WITH(j.patch(patch), "array index 4 is out of range");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.406] array index 4 is out of range");
             }
         }
 
@@ -13492,16 +13723,16 @@
             {
                 json j;
                 json patch = {{{"op", "remove"}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'remove' must have string member 'path'");
             }
 
             SECTION("non-string 'path'")
             {
                 json j;
                 json patch = {{{"op", "remove"}, {"path", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'remove' must have string member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'remove' must have string member 'path'");
             }
 
             SECTION("nonexisting target location (array)")
@@ -13524,8 +13755,8 @@
             {
                 json j = "string";
                 json patch = {{{"op", "remove"}, {"path", ""}}};
-                CHECK_THROWS_AS(j.patch(patch), std::domain_error);
-                CHECK_THROWS_WITH(j.patch(patch), "JSON pointer has no parent");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.401] JSON pointer has no parent");
             }
         }
 
@@ -13535,24 +13766,27 @@
             {
                 json j;
                 json patch = {{{"op", "replace"}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch),
+                                  "[except.408] operation 'replace' must have string member 'path'");
             }
 
             SECTION("non-string 'path'")
             {
                 json j;
                 json patch = {{{"op", "replace"}, {"path", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have string member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch),
+                                  "[except.408] operation 'replace' must have string member 'path'");
             }
 
             SECTION("missing 'value'")
             {
                 json j;
                 json patch = {{{"op", "replace"}, {"path", ""}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'replace' must have member 'value'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch),
+                                  "[except.408] operation 'replace' must have string member 'value'");
             }
 
             SECTION("nonexisting target location (array)")
@@ -13578,32 +13812,32 @@
             {
                 json j;
                 json patch = {{{"op", "move"}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'move' must have string member 'path'");
             }
 
             SECTION("non-string 'path'")
             {
                 json j;
                 json patch = {{{"op", "move"}, {"path", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'move' must have string member 'path'");
             }
 
             SECTION("missing 'from'")
             {
                 json j;
                 json patch = {{{"op", "move"}, {"path", ""}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have member 'from'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'move' must have string member 'from'");
             }
 
             SECTION("non-string 'from'")
             {
                 json j;
                 json patch = {{{"op", "move"}, {"path", ""}, {"from", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'move' must have string member 'from'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'move' must have string member 'from'");
             }
 
             SECTION("nonexisting from location (array)")
@@ -13629,32 +13863,32 @@
             {
                 json j;
                 json patch = {{{"op", "copy"}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'copy' must have string member 'path'");
             }
 
             SECTION("non-string 'path'")
             {
                 json j;
                 json patch = {{{"op", "copy"}, {"path", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'copy' must have string member 'path'");
             }
 
             SECTION("missing 'from'")
             {
                 json j;
                 json patch = {{{"op", "copy"}, {"path", ""}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have member 'from'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'copy' must have string member 'from'");
             }
 
             SECTION("non-string 'from'")
             {
                 json j;
                 json patch = {{{"op", "copy"}, {"path", ""}, {"from", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'copy' must have string member 'from'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'copy' must have string member 'from'");
             }
 
             SECTION("nonexisting from location (array)")
@@ -13680,24 +13914,24 @@
             {
                 json j;
                 json patch = {{{"op", "test"}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'test' must have string member 'path'");
             }
 
             SECTION("non-string 'path'")
             {
                 json j;
                 json patch = {{{"op", "test"}, {"path", 1}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have string member 'path'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'test' must have string member 'path'");
             }
 
             SECTION("missing 'value'")
             {
                 json j;
                 json patch = {{{"op", "test"}, {"path", ""}}};
-                CHECK_THROWS_AS(j.patch(patch), std::invalid_argument);
-                CHECK_THROWS_WITH(j.patch(patch), "operation 'test' must have member 'value'");
+                CHECK_THROWS_AS(j.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(j.patch(patch), "[except.408] operation 'test' must have string member 'value'");
             }
         }
     }
@@ -13889,8 +14123,8 @@
                 )"_json;
 
                 // the test will fail
-                CHECK_THROWS_AS(doc.patch(patch), std::domain_error);
-                CHECK_THROWS_WITH(doc.patch(patch), "unsuccessful: " + patch[0].dump());
+                CHECK_THROWS_AS(doc.patch(patch), json::extension_error);
+                CHECK_THROWS_WITH(doc.patch(patch), "[except.410] unsuccessful: " + patch[0].dump());
             }
         }
     }