nlohmann / nlohmann/json

std::optional<T> direct construction from JSON null throws instead of yielding std::nullopt

Open
#5,246 2 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
C++
Stars
50.6k
Forks
7.5k
Avg merge
4d 17h
Merged PRs (30d)
58

Description

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:

  1. std::optional<T> has its own converting constructor that accepts any type constructible from T
  2. T (e.g., std::string) is constructible from basic_json via basic_json::operator T()
  3. When both constructors are viable, std::optional's own constructor wins overload resolution
  4. 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

  • #4864: Original std::optional support issue (now fixed on develop)
  • #3453: Type-strictness redesign for 4.0 (where a permanent fix could live)

Note: This issue was filed by Claude Code to track a limitation separate from #4864.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading the conversion operator in json.hpp:1946-1971 and compare direct std::optional construction with the documented .get<std::optional>() and get_to() workarounds. Review related issues #4864 and #3453 to determine whether this limitation belongs in a future type-strictness redesign; done means establishing whether a non-breaking fix is possible or documenting the limitation and workaround.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
backend-api-design
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.