Skip to content

NLOHMANN_JSON_SERIALIZE_ENUM_STRICT: from_json's message breaks for invalid UTF-8 and custom string_t #5667

Description

@nlohmann

Description

The from_json generated by NLOHMANN_JSON_SERIALIZE_ENUM_STRICT builds its error message with "..." + j.dump(). This has two consequences:

  1. If the unmatched JSON value is (or contains) a string with invalid UTF-8, j.dump() throws type_error.316 while the message is being built, so the caller gets type_error.316 instead of the documented out_of_range.410. Such strings reach get<Enum>() for example from from_cbor()/from_msgpack(), which do not validate UTF-8 (from_cbor()/from_msgpack() do not validate UTF-8 in text strings at decode time (only dump() does) #5529), or from strings assigned by the program.
  2. With a custom string_t, j.dump() returns string_t, and const char* + string_t does not compile unless the type happens to provide that operator+ (not among the documented StringType requirements), and the result must also convert to the const std::string& that out_of_range::create takes. For example, get<Color>() with the alt_json type from tests/src/unit-alt-string.cpp fails with error: invalid operands to binary expression ('const char[36]' and 'string_t' (aka 'alt_string')) at macro_scope.hpp:339.

The code is at include/nlohmann/detail/macro_scope.hpp#L339:

else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410,"enum value out of range for " #ENUM_TYPE ": " + j.dump(), &j));

The NLOHMANN_JSON_SERIALIZE_ENUM_STRICT documentation says: "Undefined input throws out_of_range.410 in both directions".

Since when: the macro was added in #5151 (58cfecf, 2026-05-19) and is not part of a release yet.

Possible fix: build the message with detail::concat (which appends any type with data()/size()) and a dump() that cannot throw on invalid UTF-8. I tested this with the program below (C++11 and C++17) and with the alt_json type from tests/src/unit-alt-string.cpp; the messages checked in tests/src/unit-conversions.cpp are unchanged for valid strings, but I did not run the test suite:

else templated_json_throw<nlohmann::detail::out_of_range>(nlohmann::detail::out_of_range::create(410, nlohmann::detail::concat("enum value out of range for " #ENUM_TYPE ": ", j.dump(-1, ' ', false, nlohmann::detail::error_handler_t::replace)), &j));

With it, the second case prints [json.exception.out_of_range.410] enum value out of range for Color: "<U+FFFD>", where <U+FFFD> stands for the replacement character written as raw UTF-8.

Reproduction steps

  1. Save the program below as c08.cpp.
  2. Compile it against develop: clang++ -std=c++17 -g -O1 -fsanitize=address,undefined -fno-sanitize-recover=undefined -I include c08.cpp -o c08
  3. Run ./c08.

Expected vs. actual results

  • Expected: both conversions throw out_of_range.410; with a custom string_t the macro compiles.
  • Actual: the second conversion throws type_error.316; with a custom string_t (for example the test suite's alt_string) the from_json does not compile.

Minimal code example

#include <nlohmann/json.hpp>
#include <iostream>

using json = nlohmann::json;

enum class Color { red, green };

NLOHMANN_JSON_SERIALIZE_ENUM_STRICT(Color,
{
    {Color::red, "red"},
    {Color::green, "green"},
})

int main()
{
    // unknown value: out_of_range.410 as documented
    try
    {
        json("blue").get<Color>();
    }
    catch (const json::exception& e)
    {
        std::cout << e.what() << '\n';
    }

    // unknown value that is a string with invalid UTF-8 (e.g., from from_cbor(), see #5529)
    // expected: out_of_range.410
    // actual:   type_error.316, thrown by the j.dump() that builds the message
    try
    {
        json("\xFF").get<Color>();
    }
    catch (const json::exception& e)
    {
        std::cout << e.what() << '\n';
    }
}

Error messages

[json.exception.out_of_range.410] enum value out of range for Color: "blue"
[json.exception.type_error.316] invalid UTF-8 byte at index 0: 0xFF

Compiler and operating system

Apple clang 21.0.0 (clang-2100.3.34.2), macOS 27.0 (arm64); the custom string_t compile error also with GCC 16.2 (/opt/homebrew/bin/g++-16)

Library version

develop @ 633de8e

Validation

  • The bug also occurs if the latest version from the develop branch is used.
  • I can successfully compile and run the unit tests.

This issue was written by Claude Code on behalf of @nlohmann.

Activity

  1. self-assigned this
    on Sep 29, 2026
  2. added this to the Release 3.13.0 milestone on Sep 30, 2026
  3. added a commit that references this issue on Sep 30, 2026
    44a88d8
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

kind: bugsolution: proposed fixa fix for the issue has been proposed and waits for confirmation

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions