Repository navigation
Description
Title
RichText deserialization fails for plain strings: “JSON value does not match Telegram union”
Description
Summary
tgbot-cpp 1.13 throws std::invalid_argument when deserializing a RichText value represented as a plain JSON string.
I encountered this while calling TgLongPoll::start(). The debugger shows that a paragraph object's required text field contains a string, but RichText::value only includes formatted-object alternatives.
Environment
- tgbot-cpp: 1.13, installed through Conan 2
- Operating system: Linux
- Application language standard: C++23
- nlohmann/json: 3.12.0
- Reproduced with a Debug build of tgbot-cpp
Minimal Reproduction
The following example isolates the failing conversion from polling and requires no bot token:
#include <tgbot/Json.h>
#include <tgbot/Types.h>
#include <exception>
#include <iostream>
int main()
{
const nlohmann::json input = "plain text";
TgBot::RichText value;
try
{
TgBot::from_json(input, value);
}
catch (const std::exception& exception)
{
std::cerr << exception.what() << '\n';
return 1;
}
}The corresponding paragraph JSON observed during debugging has this structure:
{
"type": "paragraph",
"text": "plain text"
}The standalone reproduction above is derived from the debugger findings; I have not run it separately yet.
Expected Behavior
According to the Telegram Bot API documentation, RichText can be:
- A string for plain text.
- An array of
RichText. - One of the listed formatted-text objects.
Valid strings should deserialize successfully, including when nested in a paragraph's text field. Arrays should also be supported.
Actual Behavior
Deserialization throws:
JSON value does not match Telegram union
The relevant call sequence, omitting the repeated variant-decoding frames, is:
Json::readRequiredField(..., "text", ...)
→ Json::decode<std::shared_ptr<RichText>>(...)
→ from_json(..., RichText&)
→ Json::decode<0>(..., variant)
→ ... attempts all 26 alternatives ...
→ Json::decode<26>(...)
→ throws std::invalid_argument
At the failing breakpoint:
- The JSON value is a string.
- The containing object has
type = "paragraph". - The destination variant remains at its default alternative, an empty
std::shared_ptr<RichTextBold>.
Suspected Cause
RichText::value contains 26 formatted-object alternatives, but no alternative for std::string or an array of RichText.
The generic variant decoder therefore cannot match a plain string. Its discriminator checks reject each formatted-object candidate, eventually producing the generic union exception.
The RichText definition in master also appeared to have the same omission when checked.
Impact and Suggested Fix
The exception prevents the polling iteration from completing successfully. Retrying without fixing the conversion can repeatedly encounter the same pending update.
Please consider adding plain-string and recursive-array representations to RichText, ensuring both JSON decoding and encoding support them, and updating the API generator so the fix survives regeneration.
Regression tests covering strings, arrays, formatted objects, and a paragraph containing plain text would help prevent recurrence.
Activity
reo7sp commentedon Nov 3, 2026
Fixed #370