Description
Constructing or assigning std::optional<T> directly from a JSON null value throws type_error instead of yielding std::nullopt. Only the explicit .get<std::optional<T>>() form works correctly.
Reproduction
#include <nlohmann/json.hpp>
#include <optional>
int main() {
nlohmann::json j_null;
// Works correctly
auto opt1 = j_null.get<std::optional<std::string>>(); // → std::nullopt ✓
// Throws type_error 302
std::optional<std::string> opt2 = j_null; // ✗ throws
std::optional<std::string> opt3(j_null); // ✗ throws (direct-init also fails)
}
Error: [json.exception.type_error.302] type must be string, but is null
Expected behavior
Direct construction/assignment should yield std::nullopt, consistent with .get<std::optional<T>>().
Environment
- Compiler: GCC 16.1.0, Apple Clang 21.0.0
- C++ standard: C++17 and later
- Library version:
develop branch
Why this cannot be cleanly fixed
This is a limitation at the C++ language level, not a library bug:
std::optional<T> has its own converting constructor that accepts any type constructible from T
T (e.g., std::string) is constructible from basic_json via basic_json::operator T()
- When both constructors are viable, std::optional's own constructor wins overload resolution
- There is no SFINAE trick to distinguish "being called from inside std::optional's own constructor" from a direct call
A proper fix would require restricting or redesigning basic_json::operator ValueType() (json.hpp:1946-1971), which is relied upon for all ordinary implicit conversions (e.g., std::string s = json_obj;). Such a change would be a breaking change to the core conversion API and belongs in a major-version redesign.
Workaround
Use .get<std::optional<T>>() or .get_to() for std::optional<T> conversions:
auto opt = j_null.get<std::optional<std::string>>(); // → std::nullopt ✓
j_null.get_to(opt); // → std::nullopt ✓
Related
Note: This issue was filed by Claude Code to track a limitation separate from #4864.
Description
Constructing or assigning
std::optional<T>directly from a JSONnullvalue throwstype_errorinstead of yieldingstd::nullopt. Only the explicit.get<std::optional<T>>()form works correctly.Reproduction
Error:
[json.exception.type_error.302] type must be string, but is nullExpected behavior
Direct construction/assignment should yield
std::nullopt, consistent with.get<std::optional<T>>().Environment
developbranchWhy this cannot be cleanly fixed
This is a limitation at the C++ language level, not a library bug:
std::optional<T>has its own converting constructor that accepts any type constructible fromTT(e.g.,std::string) is constructible frombasic_jsonviabasic_json::operator T()A proper fix would require restricting or redesigning
basic_json::operator ValueType()(json.hpp:1946-1971), which is relied upon for all ordinary implicit conversions (e.g.,std::string s = json_obj;). Such a change would be a breaking change to the core conversion API and belongs in a major-version redesign.Workaround
Use
.get<std::optional<T>>()or.get_to()forstd::optional<T>conversions:Related
std::optionalsupport issue (now fixed ondevelop)Note: This issue was filed by Claude Code to track a limitation separate from #4864.